44 "context"
55 "encoding/base64"
66 "net/http"
7- "time"
87
8+ "connectrpc.com/connect"
99 "github.com/google/uuid"
1010 "github.com/porter-dev/api-contracts/generated/go/helpers"
1111 porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
@@ -41,18 +41,25 @@ func NewCreateAppTemplateHandler(
4141 }
4242}
4343
44+ // Base64AddonWithEnvVars is a struct that contains a base64 encoded addon proto and its env vars
45+ // These env vars will be used to create an env group that is attached to the addon
46+ type Base64AddonWithEnvVars struct {
47+ Base64Addon string `json:"base64_addon"`
48+ Variables map [string ]string `json:"variables"`
49+ Secrets map [string ]string `json:"secrets"`
50+ }
51+
4452// CreateAppTemplateRequest is the request object for the /app-template POST endpoint
4553type CreateAppTemplateRequest struct {
46- B64AppProto string `json:"b64_app_proto"`
47- Variables map [string ]string `json:"variables"`
48- Secrets map [string ]string `json:"secrets"`
49- BaseDeploymentTargetID string `json:"base_deployment_target_id"`
54+ B64AppProto string `json:"b64_app_proto"`
55+ Variables map [string ]string `json:"variables"`
56+ Secrets map [string ]string `json:"secrets"`
57+ BaseDeploymentTargetID string `json:"base_deployment_target_id"`
58+ Addons []Base64AddonWithEnvVars `json:"addons"`
5059}
5160
5261// CreateAppTemplateResponse is the response object for the /app-template POST endpoint
53- type CreateAppTemplateResponse struct {
54- AppTemplateID string `json:"app_template_id"`
55- }
62+ type CreateAppTemplateResponse struct {}
5663
5764// ServeHTTP creates or updates an app template for a given porter app
5865func (c * CreateAppTemplateHandler ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
@@ -108,115 +115,60 @@ func (c *CreateAppTemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
108115 return
109116 }
110117
111- porterApps , err := c .Repo ().PorterApp ().ReadPorterAppsByProjectIDAndName (project .ID , appName )
112- if err != nil {
113- err := telemetry .Error (ctx , span , err , "error getting porter app from repo" )
114- c .HandleAPIError (w , r , apierrors .NewErrPassThroughToClient (err , http .StatusBadRequest ))
115- return
116- }
117- if len (porterApps ) == 0 {
118- err := telemetry .Error (ctx , span , err , "no porter apps returned" )
119- c .HandleAPIError (w , r , apierrors .NewErrPassThroughToClient (err , http .StatusBadRequest ))
120- return
121- }
122- if len (porterApps ) > 1 {
123- err := telemetry .Error (ctx , span , err , "multiple porter apps returned; unable to determine which one to use" )
124- c .HandleAPIError (w , r , apierrors .NewErrPassThroughToClient (err , http .StatusBadRequest ))
125- return
126- }
127-
128- if porterApps [0 ].ID == 0 {
129- err := telemetry .Error (ctx , span , err , "porter app id is missing" )
130- c .HandleAPIError (w , r , apierrors .NewErrPassThroughToClient (err , http .StatusInternalServerError ))
131- return
132- }
133-
134- telemetry .WithAttributes (span , telemetry.AttributeKV {Key : "porter-app-id" , Value : porterApps [0 ].ID })
135-
136- var appTemplate * models.AppTemplate
137-
138- existingAppTemplate , err := c .Repo ().AppTemplate ().AppTemplateByPorterAppID (
139- project .ID ,
140- porterApps [0 ].ID ,
141- )
142- if err != nil {
143- err := telemetry .Error (ctx , span , err , "error checking for existing app template" )
144- c .HandleAPIError (w , r , apierrors .NewErrPassThroughToClient (err , http .StatusInternalServerError ))
145- return
146- }
147-
148- if existingAppTemplate .ID != uuid .Nil {
149- appTemplate = existingAppTemplate
150- telemetry .WithAttributes (span , telemetry.AttributeKV {Key : "update-app-template" , Value : true })
151- }
152- if appTemplate == nil {
153- appTemplate = & models.AppTemplate {
154- ProjectID : int (project .ID ),
155- PorterAppID : int (porterApps [0 ].ID ),
156- }
157- telemetry .WithAttributes (span , telemetry.AttributeKV {Key : "update-app-template" , Value : false })
158- }
159-
160118 protoWithoutDefaultAppEnvGroups , err := filterDefaultAppEnvGroups (ctx , request .B64AppProto , agent )
161119 if err != nil {
162120 err := telemetry .Error (ctx , span , err , "error filtering default app env groups" )
163121 c .HandleAPIError (w , r , apierrors .NewErrPassThroughToClient (err , http .StatusInternalServerError ))
164122 return
165123 }
166124
167- appTemplate .Base64App = protoWithoutDefaultAppEnvGroups
168- appTemplate .BaseDeploymentTargetID = baseDeploymentTarget
169-
170- updatedAppTemplate , err := c .Repo ().AppTemplate ().CreateAppTemplate (appTemplate )
171- if err != nil {
172- err := telemetry .Error (ctx , span , err , "error creating app template" )
173- c .HandleAPIError (w , r , apierrors .NewErrPassThroughToClient (err , http .StatusInternalServerError ))
174- return
175- }
125+ var addonTemplates []* porterv1.AddonWithEnvVars
126+ for _ , addon := range request .Addons {
127+ decoded , err := base64 .StdEncoding .DecodeString (addon .Base64Addon )
128+ if err != nil {
129+ err := telemetry .Error (ctx , span , err , "error decoding base64 addon" )
130+ c .HandleAPIError (w , r , apierrors .NewErrPassThroughToClient (err , http .StatusInternalServerError ))
131+ return
132+ }
176133
177- if updatedAppTemplate == nil {
178- err := telemetry .Error (ctx , span , err , "updated app template is nil" )
179- c .HandleAPIError (w , r , apierrors .NewErrPassThroughToClient (err , http .StatusInternalServerError ))
180- return
181- }
182- if updatedAppTemplate .ID == uuid .Nil {
183- err := telemetry .Error (ctx , span , err , "updated app template id is nil" )
184- c .HandleAPIError (w , r , apierrors .NewErrPassThroughToClient (err , http .StatusInternalServerError ))
185- return
186- }
134+ addonProto := & porterv1.Addon {}
135+ err = helpers .UnmarshalContractObject (decoded , addonProto )
136+ if err != nil {
137+ err := telemetry .Error (ctx , span , err , "error unmarshalling addon proto" )
138+ c .HandleAPIError (w , r , apierrors .NewErrPassThroughToClient (err , http .StatusInternalServerError ))
139+ return
140+ }
187141
188- previewTemplateEnvName , err := porter_app .AppTemplateEnvGroupName (ctx , appName , cluster .ID , c .Repo ().PorterApp ())
189- if err != nil {
190- err := telemetry .Error (ctx , span , err , "unable to get app template env group name" )
191- c .HandleAPIError (w , r , apierrors .NewErrPassThroughToClient (err , http .StatusInternalServerError ))
192- return
193- }
142+ addonTemplates = append (addonTemplates , & porterv1.AddonWithEnvVars {
143+ Addon : addonProto ,
144+ EnvVars : & porterv1.EnvGroupVariables {
145+ Normal : addon .Variables ,
146+ Secret : addon .Secrets ,
147+ },
148+ })
149+ }
150+
151+ updateAppTemplateReq := connect .NewRequest (& porterv1.UpdateAppTemplateRequest {
152+ ProjectId : int64 (project .ID ),
153+ AppName : appName ,
154+ AppTemplate : protoWithoutDefaultAppEnvGroups ,
155+ AppEnv : & porterv1.EnvGroupVariables {
156+ Normal : request .Variables ,
157+ Secret : request .Secrets ,
158+ },
159+ AddonTemplates : addonTemplates ,
160+ BaseDeploymentTargetId : baseDeploymentTarget .String (),
161+ })
194162
195- envGroup , err := environment_groups . LatestBaseEnvironmentGroup ( ctx , agent , previewTemplateEnvName )
163+ updateAppTemplateRes , err := c . Config (). ClusterControlPlaneClient . UpdateAppTemplate ( ctx , updateAppTemplateReq )
196164 if err != nil {
197- err := telemetry .Error (ctx , span , err , "unable to get latest base environment group " )
165+ err := telemetry .Error (ctx , span , err , "error updating app template " )
198166 c .HandleAPIError (w , r , apierrors .NewErrPassThroughToClient (err , http .StatusInternalServerError ))
199167 return
200168 }
201169
202- if envGroup .Name == "" {
203- envGroup = environment_groups.EnvironmentGroup {
204- Name : previewTemplateEnvName ,
205- CreatedAtUTC : time .Now ().UTC (),
206- }
207- }
208- envGroup .Variables = request .Variables
209- envGroup .SecretVariables = request .Secrets
210-
211- additionalEnvGroupLabels := map [string ]string {
212- LabelKey_AppName : appName ,
213- environment_groups .LabelKey_DefaultAppEnvironment : "true" ,
214- LabelKey_PorterManaged : "true" ,
215- }
216-
217- err = environment_groups .CreateOrUpdateBaseEnvironmentGroup (ctx , agent , envGroup , additionalEnvGroupLabels )
218- if err != nil {
219- err := telemetry .Error (ctx , span , err , "unable to create or update base environment group" )
170+ if updateAppTemplateRes == nil || updateAppTemplateRes .Msg == nil {
171+ err := telemetry .Error (ctx , span , err , "error updating app template" )
220172 c .HandleAPIError (w , r , apierrors .NewErrPassThroughToClient (err , http .StatusInternalServerError ))
221173 return
222174 }
@@ -238,45 +190,42 @@ func (c *CreateAppTemplateHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
238190 return
239191 }
240192
241- res := & CreateAppTemplateResponse {
242- AppTemplateID : updatedAppTemplate .ID .String (),
243- }
193+ res := & CreateAppTemplateResponse {}
244194
245195 c .WriteResult (w , r , res )
246196}
247197
248198// filterDefaultAppEnvGroups filters out any default app env groups found when creating an app template
249199// app templates are based on the latest version of a given app, so it is possible for this env group to be included
250200// however, the app template will get its own default env group when used to deploy to a preview environment
251- func filterDefaultAppEnvGroups (ctx context.Context , b64AppProto string , agent * kubernetes.Agent ) (string , error ) {
201+ func filterDefaultAppEnvGroups (ctx context.Context , b64AppProto string , agent * kubernetes.Agent ) (* porterv1. PorterApp , error ) {
252202 ctx , span := telemetry .NewSpan (ctx , "filter-default-app-env-groups" )
253203 defer span .End ()
254204
255- var finalAppProto string
205+ appProto := & porterv1. PorterApp {}
256206
257207 if b64AppProto == "" {
258- return finalAppProto , telemetry .Error (ctx , span , nil , "b64 app proto is empty" )
208+ return appProto , telemetry .Error (ctx , span , nil , "b64 app proto is empty" )
259209 }
260210 if agent == nil {
261- return finalAppProto , telemetry .Error (ctx , span , nil , "agent is nil" )
211+ return appProto , telemetry .Error (ctx , span , nil , "agent is nil" )
262212 }
263213
264214 decoded , err := base64 .StdEncoding .DecodeString (b64AppProto )
265215 if err != nil {
266- return finalAppProto , telemetry .Error (ctx , span , err , "error decoding base app" )
216+ return appProto , telemetry .Error (ctx , span , err , "error decoding base app" )
267217 }
268218
269- appProto := & porterv1.PorterApp {}
270219 err = helpers .UnmarshalContractObject (decoded , appProto )
271220 if err != nil {
272- return finalAppProto , telemetry .Error (ctx , span , err , "error unmarshalling app proto" )
221+ return appProto , telemetry .Error (ctx , span , err , "error unmarshalling app proto" )
273222 }
274223
275224 filteredEnvGroups := []* porterv1.EnvGroup {}
276225 for _ , envGroup := range appProto .EnvGroups {
277226 baseEnvGroup , err := environment_groups .LatestBaseEnvironmentGroup (ctx , agent , envGroup .Name )
278227 if err != nil {
279- return finalAppProto , telemetry .Error (ctx , span , err , "unable to get latest base environment group" )
228+ return appProto , telemetry .Error (ctx , span , err , "unable to get latest base environment group" )
280229 }
281230 if baseEnvGroup .DefaultAppEnvironment {
282231 continue
@@ -287,12 +236,5 @@ func filterDefaultAppEnvGroups(ctx context.Context, b64AppProto string, agent *k
287236
288237 appProto .EnvGroups = filteredEnvGroups
289238
290- encoded , err := helpers .MarshalContractObject (ctx , appProto )
291- if err != nil {
292- return finalAppProto , telemetry .Error (ctx , span , err , "error marshalling app proto" )
293- }
294-
295- finalAppProto = base64 .StdEncoding .EncodeToString (encoded )
296-
297- return finalAppProto , nil
239+ return appProto , nil
298240}
0 commit comments