1 package resolver
2
3
4
5
6
7 import (
8 "context"
9 "errors"
10 "fmt"
11
12 "edge-infra.dev/pkg/edge/api/apierror"
13 "edge-infra.dev/pkg/edge/api/graph/mapper"
14 "edge-infra.dev/pkg/edge/api/graph/model"
15 "edge-infra.dev/pkg/edge/api/utils"
16 "edge-infra.dev/pkg/edge/constants"
17 "k8s.io/apimachinery/pkg/util/validation"
18 )
19
20
21 func (r *mutationResolver) CreateHelmRelease(ctx context.Context, payload model.HelmReleasePayload) (bool, error) {
22 errors := validation.IsDNS1123Label(payload.Name)
23 if len(errors) > 0 {
24 return false, fmt.Errorf("invalid name for helm resource: %s, errors: %v", payload.Name, errors)
25 }
26 errors = validation.IsDNS1123Label(payload.Namespace)
27 if len(errors) > 0 {
28 return false, fmt.Errorf("invalid namespace for helm resource: %s, errors: %v", payload.Namespace, errors)
29 }
30
31 var (
32 cluster *model.Cluster
33 projectID string
34 err error
35 )
36 switch {
37 case !utils.IsNullOrEmpty(payload.ClusterEdgeID):
38 cluster, err = r.StoreClusterService.GetCluster(ctx, *payload.ClusterEdgeID)
39 if err != nil {
40 return false, err
41 }
42 projectID = cluster.ProjectID
43 case !utils.IsNullOrEmpty(payload.BannerEdgeID):
44 projectID, err = r.BannerService.GetBannerProjectID(ctx, *payload.BannerEdgeID)
45 if err != nil {
46 return false, fmt.Errorf("failed to get banner for workload: %w", err)
47 }
48 default:
49 return false, apierror.New("please provide bannerEdgeId or clusterEdgeId")
50 }
51
52 valid, err := r.HelmService.ValidateHelmConfigAgainstSchema(ctx, payload.ConfigValues, projectID, payload.HelmChart, payload.Version, payload.Secret)
53 if !valid {
54 return false, fmt.Errorf("error validating config value against schema: %s", err)
55 }
56
57 err = r.HelmService.CreateHelmReleaseSQL(ctx, payload, cluster)
58 if err != nil {
59 return false, err
60 }
61
62 return true, err
63 }
64
65
66 func (r *mutationResolver) DeleteHelmRelease(ctx context.Context, helmEdgeID *string, name *string, clusterEdgeID *string) (bool, error) {
67 if !utils.IsNullOrEmpty(helmEdgeID) {
68 err := r.HelmService.SoftDeleteHelmReleaseSQL(ctx, helmEdgeID, name, clusterEdgeID)
69 if err != nil {
70 return false, err
71 }
72 } else {
73 hasClusterInfo := !utils.IsNullOrEmpty(name) && !utils.IsNullOrEmpty(clusterEdgeID)
74
75 if hasClusterInfo {
76 matchedWorkloads, err := r.HelmService.GetHelmWorkloadsByName(ctx, *clusterEdgeID, *name)
77
78 if err != nil || len(matchedWorkloads) == 0 {
79 return false, fmt.Errorf("there was an issue finding workload for deletion")
80 }
81
82 if len(matchedWorkloads) == 1 {
83 err := r.HelmService.SoftDeleteHelmReleaseSQL(ctx, &matchedWorkloads[0].HelmEdgeID, &matchedWorkloads[0].Name, &matchedWorkloads[0].ClusterEdgeID)
84 if err != nil {
85 return false, err
86 }
87 } else {
88 return false, fmt.Errorf("multiple matching workload(s) found, please use helmEdgeId instead")
89 }
90 } else {
91 return false, fmt.Errorf("please provide a helmEdgeId or clusterEdgeId and workload name")
92 }
93 }
94
95 return true, nil
96 }
97
98
99 func (r *mutationResolver) CreateOrUpdateBannerHelmRepository(ctx context.Context, name string, bannerEdgeID string, url string, secret *string) (bool, error) {
100 errors := validation.IsDNS1123Label(name)
101 if len(errors) > 0 {
102 return false, fmt.Errorf("invalid name for helm repository: %s, errors: %v", name, errors)
103 }
104 projectID, err := r.BannerService.GetBannerProjectID(ctx, bannerEdgeID)
105 if err != nil {
106 return false, err
107 }
108 err = r.HelmService.CreateBannerHelmRepository(ctx, name, url, secret, projectID)
109 if err != nil {
110 return false, err
111 }
112 return true, nil
113 }
114
115
116 func (r *mutationResolver) CreateHelmRepository(ctx context.Context, name string, bannerEdgeID string, url string, username *string, password *string) (bool, error) {
117 if _, reserved := constants.PlatformHelmRepositories[name]; reserved {
118 return false, fmt.Errorf("the name %s is an edge reserved name. please use another", name)
119 }
120 errors := validation.IsDNS1123Label(name)
121 if len(errors) > 0 {
122 return false, fmt.Errorf("invalid name for helm repository: %s, errors: %v", name, errors)
123 }
124 if username != nil && password == nil {
125 return false, fmt.Errorf("password cannot be empty when username is provided")
126 }
127 if password != nil && username == nil {
128 return false, fmt.Errorf("username cannot be empty when password is provided")
129 }
130 if username == nil && password == nil {
131 username = new(string)
132 password = new(string)
133 }
134 index, err := r.HelmService.GetHelmRepositoryIndex(url, *username, *password)
135 if index == nil {
136 return false, fmt.Errorf("invalid helm repository, missing index.yaml")
137 }
138 if err != nil {
139 return false, fmt.Errorf("invalid helm repository, invalid index.yaml file")
140 }
141 projectID, err := r.BannerService.GetBannerProjectID(ctx, bannerEdgeID)
142 if err != nil {
143 return false, err
144 }
145 defaultWorkload := string(constants.TenantNamespaceSelector)
146 _, err = r.CreateOrUpdateSecretManagerSecret(ctx, name, bannerEdgeID, constants.DefaultOwnerFilter, mapper.ToHelmRepositoryKeyValues(name, url, username, password), &defaultWorkload, constants.HelmRepositorySecretType)
147 if err != nil {
148 return false, err
149 }
150 externalSecrets, err := r.HelmService.GenerateHelmReleaseExternalSecrets(ctx, projectID, mapper.HelmReleaseNamespace, []string{name})
151 if err != nil {
152 return false, err
153 }
154 if err = r.HelmService.SendExternalSecretToChariot(ctx, projectID, "", externalSecrets); err != nil {
155 return false, err
156 }
157 err = r.HelmService.CreateBannerHelmRepository(ctx, name, url, &name, projectID)
158 if err != nil {
159 return false, err
160 }
161 return true, nil
162 }
163
164
165 func (r *mutationResolver) DeleteHelmRepository(ctx context.Context, name string, bannerEdgeID string) (bool, error) {
166 if _, reserved := constants.PlatformHelmRepositories[name]; reserved {
167 return false, fmt.Errorf("the name %s is an edge reserved name. please use another", name)
168 }
169 projectID, err := r.BannerService.GetBannerProjectID(ctx, bannerEdgeID)
170 if err != nil {
171 return false, err
172 }
173 err = r.HelmService.DeleteHelmRepo(ctx, name, projectID, bannerEdgeID)
174 if err != nil {
175 return false, err
176 }
177 return r.GCPService.DeleteSecret(ctx, name, projectID)
178 }
179
180
181 func (r *mutationResolver) DeleteBannerHelmRepository(ctx context.Context, name string, bannerEdgeID string) (bool, error) {
182 return r.DeleteHelmRepository(ctx, name, bannerEdgeID)
183 }
184
185
186 func (r *mutationResolver) UpdateHelmRelease(ctx context.Context, helmEdgeID *string, helmReleaseName *string, clusterEdgeID *string, version *string, configValues *string, injectConfigmaps []model.InjectableConfigmaps, secrets []string, labelEdgeIds []string) (bool, error) {
187 var helmEdgeIDToUpdate string
188
189 switch {
190 case !utils.IsNullOrEmpty(helmEdgeID):
191 helmEdgeIDToUpdate = utils.CheckString(helmEdgeID)
192 case !utils.IsNullOrEmpty(helmReleaseName) && !utils.IsNullOrEmpty(clusterEdgeID):
193 matchedWorkloads, err := r.HelmService.GetHelmWorkloadsByName(ctx, *clusterEdgeID, *helmReleaseName)
194 if err != nil || len(matchedWorkloads) == 0 {
195 return false, fmt.Errorf("there was an issue finding workload for update")
196 }
197 if len(matchedWorkloads) == 1 {
198 helmEdgeIDToUpdate = utils.CheckString(&matchedWorkloads[0].HelmEdgeID)
199 } else {
200 return false, fmt.Errorf("multiple matching workload(s) found, please use helmEdgeId instead")
201 }
202 default:
203 return false, fmt.Errorf("please provide a helmEdgeId or helmReleaseName and clusterEdgeId")
204 }
205
206 helmWorkload, err := r.HelmService.GetHelmWorkloadSQL(ctx, helmEdgeIDToUpdate)
207 if err != nil {
208 return false, fmt.Errorf("failed to get helm workload: %s", err)
209 }
210 projectID, err := r.BannerService.GetBannerProjectID(ctx, helmWorkload.BannerEdgeID)
211 if err != nil {
212 return false, fmt.Errorf("failed to get banner project ID: %s", err)
213 }
214
215
216 newVersion := helmWorkload.HelmChartVersion
217 if version != nil {
218 newVersion = *version
219 }
220 newConfigValues := helmWorkload.ConfigValues
221 if configValues != nil {
222 newConfigValues = configValues
223 }
224 valid, err := r.HelmService.ValidateHelmConfigAgainstSchema(ctx, newConfigValues, projectID, helmWorkload.HelmChart, newVersion, helmWorkload.HelmRepoSecret)
225 if !valid {
226 return false, fmt.Errorf("error validating config value against schema: %s", err)
227 }
228
229 if secrets != nil {
230
231 for i := range secrets {
232 if secrets[i] == "" {
233
234
235 currentSecretsToDelete, err := r.HelmService.GetSecretsByHelmEdgeID(ctx, helmEdgeIDToUpdate)
236 if err != nil {
237 return false, err
238 }
239 if currentSecretsToDelete != nil {
240 _, errDeleteSecrets := r.HelmService.DeleteHelmSecrets(ctx, helmEdgeIDToUpdate, currentSecretsToDelete)
241 if errDeleteSecrets != nil {
242 return false, fmt.Errorf("failed to delete helm secrets")
243 }
244 }
245 err = r.HelmService.UpdateHelmReleaseSQL(ctx, helmEdgeIDToUpdate, newVersion, newConfigValues, injectConfigmaps)
246 if err != nil {
247 return false, err
248 }
249 return true, err
250 }
251
252 _, errSecrets := r.GCPService.GetSecrets(ctx, &secrets[i], nil, nil, false, projectID)
253 if errSecrets != nil {
254 return false, fmt.Errorf("invalid secrets")
255 }
256 }
257
258 currentSecretsToDelete, err := r.HelmService.GetSecretsByHelmEdgeID(ctx, helmEdgeIDToUpdate)
259 if err != nil {
260 return false, err
261 }
262
263
264 if currentSecretsToDelete != nil {
265 _, errDeleteSecrets := r.HelmService.DeleteHelmSecrets(ctx, helmEdgeIDToUpdate, currentSecretsToDelete)
266 if errDeleteSecrets != nil {
267 return false, fmt.Errorf("Failed to delete helm secrets")
268 }
269
270 _, errAddSecrets := r.HelmService.AddHelmSecrets(ctx, helmEdgeIDToUpdate, secrets)
271 if errAddSecrets != nil {
272 return false, fmt.Errorf("failed to add helm secrets")
273 }
274 }
275 }
276
277
278 if labelEdgeIds != nil {
279 _, errDelete := r.HelmService.DeleteWorkloadLabels(ctx, helmEdgeIDToUpdate)
280 if errDelete != nil {
281 return false, fmt.Errorf("failed to delete labels from workload")
282 }
283 if len(labelEdgeIds) > 0 {
284 _, errAdd := r.HelmService.AddWorkloadLabels(ctx, helmEdgeIDToUpdate, labelEdgeIds)
285 if errAdd != nil {
286 return false, fmt.Errorf("failed to add labels from workload: err: %w", errAdd)
287 }
288 }
289 }
290
291
292 err = r.HelmService.UpdateHelmReleaseSQL(ctx, helmEdgeIDToUpdate, newVersion, newConfigValues, injectConfigmaps)
293 if err != nil {
294 return false, err
295 }
296 return true, nil
297 }
298
299
300 func (r *mutationResolver) ValidateHelmConfig(ctx context.Context, payload model.ValidateHelmConfigParams) (bool, error) {
301 projectID, err := r.BannerService.GetBannerProjectID(ctx, payload.BannerEdgeID)
302 if err != nil {
303 return false, err
304 }
305 config, err := r.HelmService.GetDefaultConfigSchema(ctx, payload.ChartName, payload.SecretName, payload.ChartVersion, projectID)
306 if err != nil {
307 return false, apierror.New(apierror.HelmChartError).AddGenericErrorExtension(apierror.DetailedHelmError, err)
308 }
309 if config == nil {
310 return false, apierror.New(apierror.HelmChartError).AddGenericErrorExtension(apierror.DetailedHelmError, err)
311 }
312 if config.ConfigSchema == nil {
313 return false, apierror.New("No schema available for chart").AddGenericErrorExtension(apierror.DetailedHelmError, err)
314 }
315
316 return r.HelmService.ValidateHelmConfig(payload.HelmConfig, *config.ConfigSchema)
317 }
318
319
320 func (r *mutationResolver) AddWorkloadLabel(ctx context.Context, workloadLabelParameters model.WorkloadLabelInput) (bool, error) {
321 return r.HelmService.AddWorkloadLabel(ctx, workloadLabelParameters.HelmEdgeID, workloadLabelParameters.LabelEdgeID)
322 }
323
324
325 func (r *mutationResolver) AddWorkloadLabels(ctx context.Context, helmEdgeID string, labelEdgeIds []string) (bool, error) {
326 return r.HelmService.AddWorkloadLabels(ctx, helmEdgeID, labelEdgeIds)
327 }
328
329
330 func (r *mutationResolver) DeleteWorkloadLabel(ctx context.Context, workloadLabelParameters model.WorkloadLabelInput) (bool, error) {
331
332 if workloadLabelParameters.HelmEdgeID == "" && workloadLabelParameters.LabelEdgeID != "" {
333 _, errDeleteWithLabel := r.HelmService.DeleteWorkloadLabelByLabelEdgeID(ctx, workloadLabelParameters.LabelEdgeID)
334 if errDeleteWithLabel != nil {
335 return false, fmt.Errorf("failed to delete labels from workload with label id")
336 }
337 return true, nil
338 }
339
340
341 if workloadLabelParameters.HelmEdgeID != "" && workloadLabelParameters.LabelEdgeID == "" {
342
343 _, errDeleteWithHelmID := r.HelmService.DeleteWorkloadLabels(ctx, workloadLabelParameters.HelmEdgeID)
344 if errDeleteWithHelmID != nil {
345 return false, fmt.Errorf("failed to delete labels from workload with helm id")
346 }
347
348
349 } else {
350 _, errDelete := r.HelmService.DeleteWorkloadLabel(ctx, workloadLabelParameters.HelmEdgeID, workloadLabelParameters.LabelEdgeID)
351 if errDelete != nil {
352 return false, fmt.Errorf("failed to delete labels from workload")
353 }
354 }
355 return true, nil
356 }
357
358
359 func (r *queryResolver) HelmChartVersion(ctx context.Context, name string, secretName string, bannerEdgeID string) (*model.HelmChartResponse, error) {
360 projectID, err := r.BannerService.GetBannerProjectID(ctx, bannerEdgeID)
361 if err != nil {
362 return nil, err
363 }
364 chartVersion, err := r.HelmService.GetHelmChartVersion(ctx, name, secretName, projectID)
365 if err != nil {
366 return nil, apierror.New(apierror.HelmChartError).AddGenericErrorExtension(apierror.DetailedHelmError, err)
367 }
368 return chartVersion, nil
369 }
370
371
372 func (r *queryResolver) DefaultSchemaConfig(ctx context.Context, params model.HelmConfigSchemaParams) (*model.HelmConfig, error) {
373 projectID, err := r.BannerService.GetBannerProjectID(ctx, params.BannerEdgeID)
374 if err != nil {
375 return nil, err
376 }
377 config, err := r.HelmService.GetDefaultConfigSchema(ctx, params.ChartName, params.SecretName, params.ChartVersion, projectID)
378 if err != nil {
379 return nil, apierror.New(apierror.HelmChartError).AddGenericErrorExtension(apierror.DetailedHelmError, err)
380 }
381 return config, nil
382 }
383
384
385 func (r *queryResolver) HelmReleases(ctx context.Context, clusterEdgeID string) ([]*model.HelmRelease, error) {
386 return nil, errors.New("helmReleases query is deprecated. use helmWorkloads instead")
387 }
388
389
390 func (r *queryResolver) HelmReleasesStatus(ctx context.Context, clusterEdgeID string) ([]*model.HelmReleaseStatus, error) {
391 cluster, err := r.StoreClusterService.GetCluster(ctx, clusterEdgeID)
392 if err != nil {
393 return nil, err
394 }
395 return r.HelmService.GetHelmReleasesStatus(ctx, cluster)
396 }
397
398
399 func (r *queryResolver) HelmWorkloads(ctx context.Context, clusterEdgeID *string, bannerEdgeID *string) ([]*model.HelmWorkload, error) {
400 return r.HelmService.GetHelmWorkloads(ctx, clusterEdgeID, bannerEdgeID, true)
401 }
402
403
404 func (r *queryResolver) HelmWorkload(ctx context.Context, helmEdgeID string, clusterEdgeID string) (*model.HelmWorkload, error) {
405 return r.HelmService.GetHelmWorkload(ctx, helmEdgeID, clusterEdgeID)
406 }
407
408
409 func (r *queryResolver) HelmCharts(ctx context.Context, secretName string, bannerEdgeID string) ([]*model.HelmChart, error) {
410 projectID, err := r.BannerService.GetBannerProjectID(ctx, bannerEdgeID)
411 if err != nil {
412 return nil, err
413 }
414 charts, err := r.HelmService.GetHelmCharts(ctx, secretName, projectID)
415 if err != nil {
416 return nil, apierror.New(apierror.HelmChartError).AddGenericErrorExtension(apierror.DetailedHelmError, err)
417 }
418 return charts, err
419 }
420
421
422 func (r *queryResolver) HelmRepositories(ctx context.Context, bannerEdgeID string) ([]*model.HelmRepository, error) {
423 defaultOwnerFilter := constants.DefaultOwnerFilter
424 helmRepoType := constants.HelmRepositorySecretType
425 projectID, err := r.BannerService.GetBannerProjectID(ctx, bannerEdgeID)
426 if err != nil {
427 return nil, err
428 }
429 helmRepoSecrets, err := r.GCPService.GetSecrets(ctx, nil, &defaultOwnerFilter, &helmRepoType, true, projectID)
430 if err != nil {
431 return nil, apierror.New(apierror.HelmChartError).AddGenericErrorExtension(apierror.DetailedHelmError, err)
432 }
433 return mapper.ToHelmRepositoriesModels(helmRepoSecrets), nil
434 }
435
436
437 func (r *queryResolver) HelmRepositoryInfo(ctx context.Context, params model.HelmConfigSchemaParams) (*model.HelmRepositoryInfo, error) {
438 projectID, err := r.BannerService.GetBannerProjectID(ctx, params.BannerEdgeID)
439 if err != nil {
440 return nil, err
441 }
442 repoInfo, err := r.HelmService.GetHelmRepositoryInfo(ctx, params, projectID)
443 if err != nil {
444 return nil, apierror.New(apierror.HelmChartError).AddGenericErrorExtension(apierror.DetailedHelmError, err)
445 }
446 return repoInfo, nil
447 }
448
View as plain text