@@ -2,17 +2,23 @@ package api
22
33import (
44 "context"
5+ "fmt"
56 "net/http"
67
78 "github.com/go-gorp/gorp"
89 "github.com/gorilla/mux"
910
11+ "github.com/ovh/cds/engine/api/application"
12+ "github.com/ovh/cds/engine/api/ascode"
1013 "github.com/ovh/cds/engine/api/environment"
1114 "github.com/ovh/cds/engine/api/event"
15+ "github.com/ovh/cds/engine/api/keys"
16+ "github.com/ovh/cds/engine/api/operation"
1217 "github.com/ovh/cds/engine/api/project"
1318 "github.com/ovh/cds/engine/api/workflow"
1419 "github.com/ovh/cds/engine/service"
1520 "github.com/ovh/cds/sdk"
21+ "github.com/ovh/cds/sdk/exportentities"
1622 "github.com/ovh/cds/sdk/log"
1723)
1824
@@ -73,6 +79,30 @@ func (api *API) getEnvironmentHandler() service.Handler {
7379 env .Usage .Workflows = wf
7480 }
7581
82+ if env .FromRepository != "" {
83+ proj , err := project .Load (api .mustDB (), projectKey ,
84+ project .LoadOptions .WithApplicationWithDeploymentStrategies ,
85+ project .LoadOptions .WithPipelines ,
86+ project .LoadOptions .WithEnvironments ,
87+ project .LoadOptions .WithIntegrations )
88+ if err != nil {
89+ return err
90+ }
91+
92+ wkAscodeHolder , err := workflow .LoadByRepo (ctx , api .mustDB (), * proj , env .FromRepository , workflow.LoadOptions {
93+ WithTemplate : true ,
94+ })
95+ if err != nil && ! sdk .ErrorIs (err , sdk .ErrNotFound ) {
96+ return sdk .NewErrorFrom (err , "cannot found workflow holder of the environment" )
97+ }
98+ env .WorkflowAscodeHolder = wkAscodeHolder
99+
100+ // FIXME from_repository should never be set if the workflow holder was deleted
101+ if env .WorkflowAscodeHolder == nil {
102+ env .FromRepository = ""
103+ }
104+ }
105+
76106 return service .WriteJSON (w , env , http .StatusOK )
77107 }
78108}
@@ -194,6 +224,111 @@ func (api *API) deleteEnvironmentHandler() service.Handler {
194224 }
195225}
196226
227+ func (api * API ) updateAsCodeEnvironmentHandler () service.Handler {
228+ return func (ctx context.Context , w http.ResponseWriter , r * http.Request ) error {
229+ // Get pipeline and action name in URL
230+ vars := mux .Vars (r )
231+ key := vars [permProjectKey ]
232+ environmentName := vars ["environmentName" ]
233+
234+ branch := FormString (r , "branch" )
235+ message := FormString (r , "message" )
236+
237+ if branch == "" || message == "" {
238+ return sdk .NewErrorFrom (sdk .ErrWrongRequest , "missing branch or message data" )
239+ }
240+
241+ var env sdk.Environment
242+ if err := service .UnmarshalBody (r , & env ); err != nil {
243+ return err
244+ }
245+
246+ // check application name pattern
247+ regexp := sdk .NamePatternRegex
248+ if ! regexp .MatchString (env .Name ) {
249+ return sdk .WrapError (sdk .ErrInvalidApplicationPattern , "Environment name %s do not respect pattern" , env .Name )
250+ }
251+
252+ proj , err := project .Load (api .mustDB (), key , project .LoadOptions .WithClearKeys )
253+ if err != nil {
254+ return err
255+ }
256+
257+ envDB , err := environment .LoadEnvironmentByName (api .mustDB (), key , environmentName )
258+ if err != nil {
259+ return sdk .WrapError (err , "cannot load environment %s" , environmentName )
260+ }
261+
262+ if envDB .FromRepository == "" {
263+ return sdk .NewErrorFrom (sdk .ErrForbidden , "current environment is not ascode" )
264+ }
265+
266+ wkHolder , err := workflow .LoadByRepo (ctx , api .mustDB (), * proj , envDB .FromRepository , workflow.LoadOptions {
267+ WithTemplate : true ,
268+ })
269+ if err != nil {
270+ return err
271+ }
272+ if wkHolder .TemplateInstance != nil {
273+ return sdk .NewErrorFrom (sdk .ErrForbidden , "cannot edit an application that was generated by a template" )
274+ }
275+
276+ var rootApp * sdk.Application
277+ if wkHolder .WorkflowData .Node .Context != nil && wkHolder .WorkflowData .Node .Context .ApplicationID != 0 {
278+ rootApp , err = application .LoadByIDWithClearVCSStrategyPassword (api .mustDB (), wkHolder .WorkflowData .Node .Context .ApplicationID )
279+ if err != nil {
280+ return err
281+ }
282+ }
283+ if rootApp == nil {
284+ return sdk .NewErrorFrom (sdk .ErrWrongRequest , "cannot find the root application of the workflow %s that hold the pipeline" , wkHolder .Name )
285+ }
286+
287+ // create keys
288+ for i := range env .Keys {
289+ k := & env .Keys [i ]
290+ newKey , err := keys .GenerateKey (k .Name , k .Type )
291+ if err != nil {
292+ return err
293+ }
294+ k .Public = newKey .Public
295+ k .Private = newKey .Private
296+ k .KeyID = newKey .KeyID
297+ }
298+
299+ u := getAPIConsumer (ctx )
300+ env .ProjectID = proj .ID
301+ envExported , err := environment .ExportEnvironment (api .mustDB (), env , project .EncryptWithBuiltinKey , fmt .Sprintf ("env:%d:%s" , envDB .ID , branch ))
302+ if err != nil {
303+ return err
304+ }
305+ wp := exportentities.WorkflowComponents {
306+ Environments : []exportentities.Environment {envExported },
307+ }
308+
309+ ope , err := operation .PushOperationUpdate (ctx , api .mustDB (), api .Cache , * proj , wp , rootApp .VCSServer , rootApp .RepositoryFullname , branch , message , rootApp .RepositoryStrategy , u )
310+ if err != nil {
311+ return err
312+ }
313+
314+ sdk .GoRoutine (context .Background (), fmt .Sprintf ("UpdateAsCodeEnvironmentHandler-%s" , ope .UUID ), func (ctx context.Context ) {
315+ ed := ascode.EntityData {
316+ FromRepo : envDB .FromRepository ,
317+ Type : ascode .EnvironmentEvent ,
318+ ID : envDB .ID ,
319+ Name : envDB .Name ,
320+ OperationUUID : ope .UUID ,
321+ }
322+ asCodeEvent := ascode .UpdateAsCodeResult (ctx , api .mustDB (), api .Cache , * proj , wkHolder .ID , * rootApp , ed , u )
323+ if asCodeEvent != nil {
324+ event .PublishAsCodeEvent (ctx , proj .Key , * asCodeEvent , u )
325+ }
326+ }, api .PanicDump ())
327+
328+ return service .WriteJSON (w , ope , http .StatusOK )
329+ }
330+ }
331+
197332func (api * API ) updateEnvironmentHandler () service.Handler {
198333 return func (ctx context.Context , w http.ResponseWriter , r * http.Request ) error {
199334 // Get pipeline and action name in URL
0 commit comments