...

Source file src/edge-infra.dev/pkg/edge/api/graph/integration/helm_service_test.go

Documentation: edge-infra.dev/pkg/edge/api/graph/integration

     1  package integration_test
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	_ "embed"
     7  	"fmt"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"strings"
    11  	"time"
    12  
    13  	"edge-infra.dev/pkg/edge/api/apierror"
    14  	"edge-infra.dev/pkg/edge/api/graph/mapper"
    15  	"edge-infra.dev/pkg/edge/api/graph/model"
    16  	sqlquery "edge-infra.dev/pkg/edge/api/sql"
    17  	"edge-infra.dev/pkg/edge/api/utils"
    18  	"edge-infra.dev/test/framework/integration"
    19  
    20  	helmApi "github.com/fluxcd/helm-controller/api/v2"
    21  	"github.com/udacity/graphb"
    22  
    23  	"k8s.io/apimachinery/pkg/util/intstr"
    24  )
    25  
    26  const (
    27  	testHelmEdgeID = "be8536ff-d463-4aff-8fa9-fe81fec1ddc1"
    28  	helmCharts     = `
    29  apiVersion: v1
    30  entries:
    31   test-helm-chart1:
    32   - apiVersion: v2
    33     version: 0.0.1
    34     name: test-helm-chart1
    35     urls:
    36     - "http://%s/chart.tgz"
    37   test-helm-chart2:
    38   - apiVersion: v2
    39     version: 0.0.2
    40     name: test-helm-chart2
    41     urls:
    42     - "http://%s/chart.tgz"
    43  `
    44  	readme = `
    45  # Introduction
    46  
    47  This chart is a umbrella chart for emerald services including selling service, pos config etc.
    48  
    49  # Prerequisites
    50  Before deploying the chart you need to:
    51  `
    52  	updateConfigValues = `
    53  global:
    54   deploymentMode: store
    55   gcloud:
    56     serviceAccount:
    57       enabled: true
    58       secret: ewog-testing`
    59  	schemaData = `
    60  	{
    61  		"$schema": "http://json-schema.org/schema#",
    62  		"type": "object",
    63  		"properties": {
    64  			"ingress": {
    65  				"type": "object",
    66  				"properties": {
    67  					"namespace": {
    68  						"type": "string",
    69  						"default": "selling-services",
    70  						"pattern": "^[a-z]+[-]*[a-z]*$"
    71  					},
    72  					"services": {
    73  						"type": "array",
    74  						"items": {
    75  							"type": "object",
    76  							"properties": {
    77  								"service": {
    78  									"type": "object",
    79  									"properties": {
    80  										"name": {
    81  											"type": "string",
    82  											"pattern": "^[a-z]+[-]*[a-z]*$"
    83  										},
    84  										"path": {
    85  											"type": "string",
    86  											"pattern": "^[A-Za-z0-9_.\/-]*$"
    87  										},
    88  										"port": {
    89  											"type": "integer",
    90  											"pattern": "^[1-9]*$"
    91  										}
    92  									}
    93  								}
    94  							}
    95  						}
    96  					}
    97  				}
    98  			},
    99  			"selling-configuration": {
   100  				"type": "object",
   101  				"properties": {
   102  					"app": {
   103  						"type": "object",
   104  						"properties": {
   105  							"image": {
   106  								"type": "string",
   107  								"default": "ncr-emeraldedge-docker-dev.jfrog.io/selling-configuration",
   108  								"pattern": "^[A-Za-z0-9_.\/-]*$"
   109  							},
   110  							"replicaCount": {
   111  								"type": "integer",
   112  								"default": 1,
   113  								"pattern": "^[1-9]*$"
   114  							},
   115  							"version": {
   116  								"type": "string",
   117  								"default": "1.0.0",
   118  								"pattern": "^[0-9.]*$"
   119  							}
   120  						}
   121  					},
   122  					"livenessProbe": {
   123  						"type": "object",
   124  						"properties": {
   125  							"enabled": {
   126  								"type": "boolean",
   127  								"default": true
   128  							},
   129  							"healthCheckAPI": {
   130  								"type": "string",
   131  								"default": "selling-configuration/health",
   132  								"pattern": "^[A-Za-z0-9_.\/-]*$"
   133  							}
   134  						}
   135  					},
   136  					"namespace": {
   137  						"type": "string",
   138  						"default": "selling-services",
   139  						"pattern": "^[a-z]+[-]*[a-z]*$"
   140  					}
   141  				}
   142  			},
   143  			"selling-configuration-pgsql": {
   144  				"type": "object",
   145  				"properties": {
   146  					"app": {
   147  						"type": "object",
   148  						"properties": {
   149  							"image": {
   150  								"type": "string",
   151  								"default": "ncr-emeraldedge-docker-dev.jfrog.io/selling-conf-postgresql",
   152  								"pattern": "^[A-Za-z0-9_.\/-]*$"
   153  							},
   154  							"replicaCount": {
   155  								"type": "integer",
   156  								"default": 1,
   157  								"pattern": "^[1-9]*$"
   158  							},
   159  							"version": {
   160  								"type": "string",
   161  								"default": "1.0.0",
   162  								"pattern": "^[0-9.]*$"
   163  							}
   164  						}
   165  					},
   166  					"namespace": {
   167  						"type": "string",
   168  						"default": "selling-services",
   169  						"pattern": "^[a-z]+[-]*[a-z]*$"
   170  					}
   171  				}
   172  			},
   173  			"selling-engine": {
   174  				"type": "object",
   175  				"properties": {
   176  					"app": {
   177  						"type": "object",
   178  						"required": ["image", "replicaCount", "version"],
   179  						"properties": {
   180  							"image": {
   181  								"type": "string",
   182  								"default": "ncr-emeraldedge-docker-dev.jfrog.io/selling_engine",
   183  								"pattern": "^[A-Za-z0-9_.\/-]*$"
   184  							},
   185  							"replicaCount": {
   186  								"type": "integer",
   187  								"default": 1,
   188  								"pattern": "^[1-9]*$"
   189  							},
   190  							"version": {
   191  								"type": "string",
   192  								"default": "1.0.1",
   193  								"pattern": "^[0-9.]*$"
   194  							}
   195  						}
   196  					},
   197  					"livenessProbe": {
   198  						"type": "object",
   199  						"properties": {
   200  							"enabled": {
   201  								"type": "boolean",
   202  								"default": true
   203  							},
   204  							"healthCheckAPI": {
   205  								"type": "string",
   206  								"default": "selling-execution/health",
   207  								"pattern": "^[A-Za-z0-9_.\/-]*$"
   208  							}
   209  						}
   210  					},
   211  					"namespace": {
   212  						"type": "string",
   213  						"default": "selling-services",
   214  						"pattern": "^[a-z]+[-]*[a-z]*$"
   215  					}
   216  				}
   217  			}
   218  		}
   219  	}
   220  	`
   221  )
   222  
   223  //go:embed testdata/alpine-0.2.0.tgz
   224  var zippedChart string
   225  
   226  func (s Suite) TestCreateHelmRelease() {
   227  	integration.SkipIf(s.Framework)
   228  	srv := s.createHelmRepoTestServer()
   229  	helmURL := srv.URL
   230  	name := fmt.Sprintf("create-helmrelease-%s", s.testTime)
   231  	var response struct{ CreateOrUpdateSecretManagerSecret bool }
   232  	mutation := createOrUpdateSecretManagerSecretMutation(testOrgBannerEdgeID, testHelmSecret, "edge", "helm-repository", "tenant", []model.KeyValues{
   233  		{Key: "helm_repo_name", Value: "podinfo"},
   234  		{Key: "helmUrl", Value: helmURL},
   235  	})
   236  	ResolverClient.MustPost(mutation, &response)
   237  	s.NotNil(response.CreateOrUpdateSecretManagerSecret)
   238  	s.Equal(response.CreateOrUpdateSecretManagerSecret, true)
   239  
   240  	var res struct{ DefaultSchemaConfig *model.HelmConfig }
   241  	query := defaultSchemaConfigQuery(testHelmSecret, testOrgBannerEdgeID, testHelmChart, testHelmVersion)
   242  	s.NoError(GraphqlRetry(query, &res, func() bool { return res.DefaultSchemaConfig != nil }))
   243  	s.NotNil(res.DefaultSchemaConfig)
   244  	s.NotEmpty(res.DefaultSchemaConfig.ConfigVals)
   245  
   246  	// Test that a helm release is created by passing in a clusterEdgeID and no bannerEdgeID
   247  	var resp struct{ CreateHelmRelease bool }
   248  	namespace := fmt.Sprintf("helmrelease-%d", time.Now().UnixNano())
   249  	mutation = s.createHelmReleaseMutation("", testClusterEdgeID, name, namespace, testHelmSecret, testHelmRepo, testHelmChart, testHelmVersion, "", "")
   250  	ResolverClient.MustPost(mutation, &resp)
   251  	s.True(resp.CreateHelmRelease)
   252  
   253  	// Test that a helm release is created by passing in a bannerEdgeID and no clusterEdgeID
   254  	namespace = fmt.Sprintf("helmrelease-%d", time.Now().UnixNano())
   255  	mutation = s.createHelmReleaseMutation(testOrgBannerEdgeID, "", name, namespace, testHelmSecret, testHelmRepo, testHelmChart, testHelmVersion, "", "")
   256  	ResolverClient.MustPost(mutation, &resp)
   257  	s.True(resp.CreateHelmRelease)
   258  
   259  	// Test that a helm release is not created if neither ID is passed into the mutation
   260  	namespace = fmt.Sprintf("helmrelease-%d", time.Now().UnixNano())
   261  	mutation = s.createHelmReleaseMutation("", "", name, namespace, testHelmSecret, testHelmRepo, testHelmChart, testHelmVersion, "", "")
   262  	s.Error(ResolverClient.Post(mutation, &resp))
   263  	s.False(resp.CreateHelmRelease)
   264  }
   265  
   266  func (s Suite) TestCreateExternalSecretHelmRelease() {
   267  	integration.SkipIf(s.Framework)
   268  	srv := s.createHelmRepoTestServer()
   269  	helmURL := srv.URL
   270  	namespace := fmt.Sprintf("secret-ns-%d", time.Now().UnixNano())
   271  	name := fmt.Sprintf("external-secret-helmrelease-%d", time.Now().UnixNano())
   272  
   273  	var resp struct{ CreateHelmRelease bool }
   274  	testSecret1 := "test-secret-1"
   275  	testSecret2 := "test-secret-2"
   276  
   277  	var response struct{ CreateOrUpdateSecretManagerSecret bool }
   278  	mutation := createOrUpdateSecretManagerSecretMutation(testOrgBannerEdgeID, testSecret1, "edge", "docker-registry", "tenant", []model.KeyValues{
   279  		{Key: "docker-username", Value: "jd250001"},
   280  		{Key: "docker-password", Value: "password1"},
   281  		{Key: "docker-server", Value: "https://example.com"},
   282  	})
   283  	ResolverClient.MustPost(mutation, &response)
   284  	s.NotNil(response.CreateOrUpdateSecretManagerSecret)
   285  	s.Equal(response.CreateOrUpdateSecretManagerSecret, true)
   286  
   287  	mutation = createOrUpdateSecretManagerSecretMutation(testOrgBannerEdgeID, testSecret2, "edge", "helm-repository", "tenant", []model.KeyValues{
   288  		{Key: "helm_repo_name", Value: "podinfo"},
   289  		{Key: "helmUrl", Value: helmURL},
   290  	})
   291  	ResolverClient.MustPost(mutation, &response)
   292  	s.NotNil(response.CreateOrUpdateSecretManagerSecret)
   293  	s.Equal(response.CreateOrUpdateSecretManagerSecret, true)
   294  
   295  	mutation = createOrUpdateSecretManagerSecretMutation(testOrgBannerEdgeID, testHelmSecret, "edge", "helm-repository", "tenant", []model.KeyValues{
   296  		{Key: "helm_repo_name", Value: "podinfo"},
   297  		{Key: "helmUrl", Value: helmURL},
   298  	})
   299  	ResolverClient.MustPost(mutation, &response)
   300  	s.NotNil(response.CreateOrUpdateSecretManagerSecret)
   301  	s.Equal(response.CreateOrUpdateSecretManagerSecret, true)
   302  
   303  	clusterEdgeID := "3396a52c-6a22-4049-9593-5a63b596a205"
   304  	mutation = s.createHelmReleaseMutation(testOrgBannerEdgeID, clusterEdgeID, name, namespace, testHelmSecret, testHelmRepo, testHelmChart, testHelmVersion, "", testSecret1, testSecret2)
   305  	ResolverClient.MustPost(mutation, &resp)
   306  	s.True(resp.CreateHelmRelease)
   307  
   308  	helmEdgeID, err := s.getHelmEdgeIDByNameAndID(s.ctx, name, nil, &testOrgBannerEdgeID)
   309  	s.NoError(err)
   310  
   311  	var res struct{ HelmWorkload *model.HelmWorkload }
   312  	query := helmWorkloadQuery(clusterEdgeID, helmEdgeID)
   313  	ResolverClient.MustPost(query, &res)
   314  	s.NotNil(res.HelmWorkload)
   315  	s.Equal(testHelmVersion, res.HelmWorkload.HelmChartVersion)
   316  	s.NotNil(res.HelmWorkload.ConfigValues)
   317  	s.Equal("", *res.HelmWorkload.ConfigValues)
   318  	s.NotNil(res.HelmWorkload.InstalledBy)
   319  	s.Equal("acct:emerald-edge-dev@testing", *res.HelmWorkload.InstalledBy)
   320  }
   321  
   322  func (s Suite) TestCreateInvalidHelmRelease() {
   323  	integration.SkipIf(s.Framework)
   324  	var resp struct{ CreateHelmRelease bool }
   325  
   326  	name := fmt.Sprintf("Create-helmrelease-%s", s.testTime)
   327  	namespace := fmt.Sprintf("helmrelease-%s", s.testTime)
   328  	mutation := s.createHelmReleaseMutation(testOrgBannerEdgeID, testClusterEdgeID, name, namespace, testHelmSecret, testHelmRepo, testHelmChart, testHelmVersion, "", "")
   329  	err := ResolverClient.Post(mutation, &resp)
   330  	s.Error(err)
   331  	s.Contains(err.Error(), "invalid name for helm resource")
   332  	s.False(resp.CreateHelmRelease)
   333  
   334  	name = fmt.Sprintf("create-helmrelease-%s", s.testTime)
   335  	namespace = fmt.Sprintf("Helmrelease-%s", s.testTime)
   336  	mutation = s.createHelmReleaseMutation(testOrgBannerEdgeID, testClusterEdgeID, name, namespace, testHelmSecret, testHelmRepo, testHelmChart, testHelmVersion, "", "")
   337  	err = ResolverClient.Post(mutation, &resp)
   338  	s.Error(err)
   339  	s.Contains(err.Error(), "invalid namespace for helm resource")
   340  	s.False(resp.CreateHelmRelease)
   341  }
   342  
   343  func (s Suite) TestDeleteHelmRelease() {
   344  	integration.SkipIf(s.Framework)
   345  
   346  	// helmEdgeID for workload named "test-deleting-helm-workload"
   347  	helmEdgeID := "29b8709a-2288-4054-829a-6dbfa8cf9eec"
   348  	var deleteResponse struct{ DeleteHelmRelease bool }
   349  	mutation := deleteHelmReleaseMutation(helmEdgeID, "", "")
   350  	ResolverClient.MustPost(mutation, &deleteResponse)
   351  	s.True(deleteResponse.DeleteHelmRelease)
   352  }
   353  
   354  func (s Suite) TestDeleteHelmRelease_Invalid() {
   355  	integration.SkipIf(s.Framework)
   356  	name := "duplicate-workload-name"
   357  
   358  	// Case 1: Neither a helmEdgeId nor name and clusterEdgeId are provided
   359  	var deleteResponse struct{ DeleteHelmRelease bool }
   360  	mutation := deleteHelmReleaseMutation("", "", "")
   361  	err := ResolverClient.Post(mutation, &deleteResponse)
   362  	s.Error(err)
   363  	s.False(deleteResponse.DeleteHelmRelease)
   364  
   365  	// Case 2: A helmReleaseName is provided but a clusterEdgeId is not provided
   366  	mutation = deleteHelmReleaseMutation("", "", name)
   367  	err = ResolverClient.Post(mutation, &deleteResponse)
   368  	s.Error(err)
   369  	s.False(deleteResponse.DeleteHelmRelease)
   370  
   371  	// Case 3: Both helmReleaseName and clusterEdgeId are provided but there are multiple matching
   372  	// workloads for the provided inputs
   373  	mutation = deleteHelmReleaseMutation("", clusterEdgeID, name)
   374  	err = ResolverClient.Post(mutation, &deleteResponse)
   375  	s.Error(err)
   376  	s.False(deleteResponse.DeleteHelmRelease)
   377  
   378  	// Case 4: A workload is deployed to multiple (more than 1) clusters via labels
   379  	//
   380  	// helmEdgeID for a workload that is deployed to 2 clusters via labels
   381  	helmEdgeID := "aa015539-3de1-4ec9-bc98-4c9dcc8841e0"
   382  	mutation = deleteHelmReleaseMutation(helmEdgeID, "", "")
   383  	err = ResolverClient.Post(mutation, &deleteResponse)
   384  	s.Error(err)
   385  	s.False(deleteResponse.DeleteHelmRelease)
   386  }
   387  
   388  func (s Suite) TestCreateOrUpdateBannerHelmRepository() {
   389  	integration.SkipIf(s.Framework)
   390  	name := fmt.Sprintf("create-helmrepo-%s", s.testTime)
   391  
   392  	var response struct{ CreateOrUpdateBannerHelmRepository bool }
   393  	mutation := createOrUpdateBannerHelmRepositoryMutation(testOrgBannerEdgeID, name, testHelmRepoURL, testHelmSecret)
   394  	ResolverClient.MustPost(mutation, &response)
   395  	s.True(response.CreateOrUpdateBannerHelmRepository, "unable to create HelmRepository")
   396  
   397  	mutation = createOrUpdateBannerHelmRepositoryMutation(testOrgBannerEdgeID, name, testHelmRepoURL, testHelmSecret)
   398  	ResolverClient.MustPost(mutation, &response)
   399  	s.True(response.CreateOrUpdateBannerHelmRepository, "unable to update HelmRepository")
   400  }
   401  
   402  func (s Suite) TestCreateOrUpdateInvalidBannerHelmRepository() {
   403  	integration.SkipIf(s.Framework)
   404  	name := fmt.Sprintf("Create-helmrepo-%s", s.testTime)
   405  
   406  	var response struct{ CreateOrUpdateBannerHelmRepository bool }
   407  	mutation := createOrUpdateBannerHelmRepositoryMutation(testOrgBannerEdgeID, name, testHelmRepoURL, testHelmSecret)
   408  	err := ResolverClient.Post(mutation, &response)
   409  	s.Error(err)
   410  	s.Contains(err.Error(), "invalid name for helm repository")
   411  	s.False(response.CreateOrUpdateBannerHelmRepository)
   412  }
   413  
   414  func (s Suite) TestDeleteBannerHelmRepository() {
   415  	integration.SkipIf(s.Framework)
   416  	name := fmt.Sprintf("delete-helmrepo-%s", s.testTime)
   417  
   418  	var response struct{ CreateOrUpdateBannerHelmRepository bool }
   419  	mutation := createOrUpdateBannerHelmRepositoryMutation(testOrgBannerEdgeID, name, testHelmRepoURL, testHelmSecret)
   420  	ResolverClient.MustPost(mutation, &response)
   421  	s.True(response.CreateOrUpdateBannerHelmRepository, "unable to create or update HelmRepository")
   422  }
   423  
   424  func (s Suite) TestUpdateHelmRelease_Invalid() {
   425  	integration.SkipIf(s.Framework)
   426  	name := "duplicate-workload-name"
   427  
   428  	// Case 1: Neither a helmEdgeID nor helmReleaseName and clusterEdgeId are provided
   429  	var updatedResponse struct{ UpdateHelmRelease bool }
   430  	mutation := updateHelmReleaseMutation("", "", "", testHelmUpdatedVersion, "", nil, nil)
   431  	err := ResolverClient.Post(mutation, &updatedResponse)
   432  	s.Error(err)
   433  	s.False(updatedResponse.UpdateHelmRelease)
   434  
   435  	// Case 2: A helmReleaseName is provided but a clusterEdgeId is not provided
   436  	mutation = updateHelmReleaseMutation("", "", name, testHelmUpdatedVersion, "", nil, nil)
   437  	err = ResolverClient.Post(mutation, &updatedResponse)
   438  	s.Error(err)
   439  	s.False(updatedResponse.UpdateHelmRelease)
   440  
   441  	// Case 3: Both helmReleaseName and clusterEdgeId are provided but there are multiple matching
   442  	// workloads for the provided inputs
   443  	mutation = updateHelmReleaseMutation("", testClusterEdgeID, name, testHelmUpdatedVersion, "", nil, nil)
   444  	err = ResolverClient.Post(mutation, &updatedResponse)
   445  	s.Error(err)
   446  	s.False(updatedResponse.UpdateHelmRelease)
   447  }
   448  
   449  func (s Suite) TestUpdateHelmRelease() {
   450  	integration.SkipIf(s.Framework)
   451  	helmURL := testHelmRepoURL
   452  	if !integration.IsIntegrationTest() {
   453  		srv := s.createHelmRepoTestServer()
   454  		helmURL = srv.URL
   455  	}
   456  
   457  	namespace := fmt.Sprintf("helmrelease-%s", s.testTime)
   458  	chartSecret := fmt.Sprintf("chart-secret-%s", s.testTime)
   459  	name := "test-helm-release"
   460  	secrets := []string{"test-secret-1", "test-secret-2"}
   461  
   462  	testLabelEdgeIDs := []string{"442f2e77-279d-45af-acae-4ec5458b7e00"}
   463  
   464  	var secretResponse struct{ CreateOrUpdateSecretManagerSecret bool }
   465  	mutation := createOrUpdateSecretManagerSecretMutation(testOrgBannerEdgeID, chartSecret, "edge-helm", "helm-repository", "tenant", []model.KeyValues{
   466  		{Key: "helm_repo_name", Value: testHelmRepo},
   467  		{Key: "helmUrl", Value: helmURL},
   468  	})
   469  	ResolverClient.MustPost(mutation, &secretResponse)
   470  	s.True(secretResponse.CreateOrUpdateSecretManagerSecret)
   471  
   472  	var secretManagerQuery struct{ SecretManagerSecret model.SecretManagerResponse }
   473  	secretQuery := getSecretManagerSecret(chartSecret, testOrgBannerEdgeID, true)
   474  	ResolverClient.MustPost(secretQuery, &secretManagerQuery)
   475  	s.Equal(len(secretManagerQuery.SecretManagerSecret.Values), 2)
   476  
   477  	var response struct{ DefaultSchemaConfig *model.HelmConfig }
   478  	query := defaultSchemaConfigQuery(chartSecret, testOrgBannerEdgeID, testHelmChart, testHelmVersion)
   479  	s.NoError(GraphqlRetry(query, &response, func() bool { return response.DefaultSchemaConfig != nil }))
   480  	s.NotNil(response.DefaultSchemaConfig)
   481  	s.NotEmpty(response.DefaultSchemaConfig.ConfigVals)
   482  
   483  	ucv := strings.ReplaceAll(updateConfigValues, "\n", "\\n")
   484  	var resp struct{ CreateHelmRelease bool }
   485  	mutation = s.createHelmReleaseMutation(testOrgBannerEdgeID, testClusterEdgeID, name, namespace, testHelmSecret, testHelmRepo, testHelmChart, testHelmVersion, "", "")
   486  	ResolverClient.MustPost(mutation, &resp)
   487  	s.True(resp.CreateHelmRelease)
   488  
   489  	helmEdgeID, err := s.getHelmEdgeIDByNameAndID(s.ctx, name, nil, &testOrgBannerEdgeID)
   490  	s.NoError(err)
   491  
   492  	// Case 1: Update workload using helmEdgeID
   493  	var updatedResponse struct{ UpdateHelmRelease bool }
   494  	mutation = updateHelmReleaseMutation(helmEdgeID, "", "", testHelmVersion, ucv, secrets, testLabelEdgeIDs)
   495  	ResolverClient.MustPost(mutation, &updatedResponse)
   496  	s.True(updatedResponse.UpdateHelmRelease)
   497  
   498  	var res struct{ HelmWorkload *model.HelmWorkload }
   499  	query = helmWorkloadQuery(testClusterEdgeID, helmEdgeID)
   500  	ResolverClient.MustPost(query, &res)
   501  	s.NotNil(res.HelmWorkload)
   502  	s.Equal(testHelmVersion, res.HelmWorkload.HelmChartVersion)
   503  	s.NotNil(res.HelmWorkload.ConfigValues)
   504  	s.NotEqual("", *res.HelmWorkload.ConfigValues)
   505  	s.NotNil(res.HelmWorkload.InstalledBy)
   506  	s.Equal("acct:emerald-edge-dev@testing", *res.HelmWorkload.InstalledBy)
   507  	s.NotEmpty(res.HelmWorkload.Secrets)
   508  	s.NotEmpty(res.HelmWorkload.Labels)
   509  
   510  	// Case 2: Update workload using helmReleaseName and clusterEdgeID
   511  	mutation = updateHelmReleaseMutation("", testClusterEdgeID, name, "", ucv, []string{}, []string{})
   512  	ResolverClient.MustPost(mutation, &updatedResponse)
   513  	s.True(updatedResponse.UpdateHelmRelease)
   514  
   515  	query = helmWorkloadQuery(testClusterEdgeID, helmEdgeID)
   516  	ResolverClient.MustPost(query, &res)
   517  	s.NotNil(res.HelmWorkload)
   518  	s.Equal(testHelmVersion, res.HelmWorkload.HelmChartVersion)
   519  	s.NotNil(res.HelmWorkload.ConfigValues)
   520  	s.NotEqual("", *res.HelmWorkload.ConfigValues)
   521  	s.NotNil(res.HelmWorkload.InstalledBy)
   522  	s.Equal("acct:emerald-edge-dev@testing", *res.HelmWorkload.InstalledBy)
   523  	s.Empty(res.HelmWorkload.Secrets)
   524  	s.Empty(res.HelmWorkload.Labels)
   525  }
   526  
   527  func (s Suite) TestHelmChartVersion() {
   528  	integration.SkipIf(s.Framework)
   529  	helmURL := testHelmRepoURL
   530  	if !integration.IsIntegrationTest() {
   531  		srv := s.createHelmRepoTestServer()
   532  		helmURL = srv.URL
   533  	}
   534  
   535  	chartSecret := fmt.Sprintf("version-chart-secret-%s", s.testTime)
   536  	var secretResponse struct{ CreateOrUpdateSecretManagerSecret bool }
   537  	mutation := createOrUpdateSecretManagerSecretMutation(testOrgBannerEdgeID, chartSecret, "edge-helm", "helm-repository", "tenant", []model.KeyValues{
   538  		{Key: "username", Value: "jd250001"},
   539  		{Key: "password", Value: "password1"},
   540  		{Key: "helmUrl", Value: helmURL},
   541  	})
   542  	ResolverClient.MustPost(mutation, &secretResponse)
   543  	s.True(secretResponse.CreateOrUpdateSecretManagerSecret)
   544  
   545  	var resp struct{ HelmChartVersion *model.HelmChartResponse }
   546  	query := helmChartVersionQuery(testHelmChart, chartSecret, testOrgBannerEdgeID)
   547  	s.NoError(GraphqlRetry(query, &resp, func() bool { return resp.HelmChartVersion != nil }))
   548  	s.NotNil(resp.HelmChartVersion)
   549  	s.NotEmpty(resp.HelmChartVersion.Name)
   550  	s.NotEmpty(resp.HelmChartVersion.Versions)
   551  }
   552  
   553  func (s Suite) TestDefaultSchemaConfig() { //nolint: dupl
   554  	integration.SkipIf(s.Framework)
   555  	helmURL := testHelmRepoURL
   556  	if !integration.IsIntegrationTest() {
   557  		srv := s.createHelmRepoTestServer()
   558  		helmURL = srv.URL
   559  	}
   560  
   561  	chartSecret := fmt.Sprintf("config-chart-secret-%s", s.testTime)
   562  	var secretResponse struct{ CreateOrUpdateSecretManagerSecret bool }
   563  	mutation := createOrUpdateSecretManagerSecretMutation(testOrgBannerEdgeID, chartSecret, "edge-helm", "helm-repository", "tenant", []model.KeyValues{
   564  		{Key: "username", Value: "jd250001"},
   565  		{Key: "password", Value: "password1"},
   566  		{Key: "helmUrl", Value: helmURL},
   567  	})
   568  	ResolverClient.MustPost(mutation, &secretResponse)
   569  	s.True(secretResponse.CreateOrUpdateSecretManagerSecret)
   570  
   571  	var resp struct{ DefaultSchemaConfig *model.HelmConfig }
   572  	query := defaultSchemaConfigQuery(chartSecret, testOrgBannerEdgeID, testHelmChart, testHelmVersion)
   573  	s.NoError(GraphqlRetry(query, &resp, func() bool { return resp.DefaultSchemaConfig != nil }))
   574  	s.NotNil(resp.DefaultSchemaConfig)
   575  	s.NotEmpty(resp.DefaultSchemaConfig.ConfigVals)
   576  }
   577  
   578  func (s Suite) TestHelmRelease() {
   579  	integration.SkipIf(s.Framework)
   580  	if !integration.IsIntegrationTest() {
   581  		srv := s.createHelmRepoTestServer()
   582  		s.NoError(createTestHelmRepoSecret(testOrgBannerEdgeID, testHelmSecret, srv.URL))
   583  	}
   584  
   585  	namespace := fmt.Sprintf("helmrelease-%s", s.testTime)
   586  	name := fmt.Sprintf("helmreleases-%s", s.testTime)
   587  
   588  	var resp struct{ CreateHelmRelease bool }
   589  	mutation := s.createHelmReleaseMutation(testOrgBannerEdgeID, testClusterEdgeID, name, namespace, testHelmSecret, testHelmRepo, testHelmChart, testHelmVersion, "", "")
   590  	ResolverClient.MustPost(mutation, &resp)
   591  
   592  	s.True(resp.CreateHelmRelease)
   593  }
   594  
   595  func (s Suite) TestHelmReleasesStatus() {
   596  	integration.SkipIf(s.Framework)
   597  	helmURL := testHelmRepoURL
   598  	if !integration.IsIntegrationTest() {
   599  		srv := s.createHelmRepoTestServer()
   600  		helmURL = srv.URL
   601  	}
   602  
   603  	chartSecret := fmt.Sprintf("chart-secret-%s", s.testTime)
   604  
   605  	var secretResponse struct{ CreateOrUpdateSecretManagerSecret bool } //nolint: dupl
   606  	mutation := createOrUpdateSecretManagerSecretMutation(testOrgBannerEdgeID, chartSecret, "edge-helm", "helm-repository", "tenant", []model.KeyValues{
   607  		{Key: "helm_repo_name", Value: testHelmRepo},
   608  		{Key: "helmUrl", Value: helmURL},
   609  	})
   610  	ResolverClient.MustPost(mutation, &secretResponse)
   611  	s.True(secretResponse.CreateOrUpdateSecretManagerSecret)
   612  
   613  	var secretManagerQuery struct{ SecretManagerSecret model.SecretManagerResponse }
   614  	secretQuery := getSecretManagerSecret(chartSecret, testOrgBannerEdgeID, true)
   615  	ResolverClient.MustPost(secretQuery, &secretManagerQuery)
   616  	s.Equal(len(secretManagerQuery.SecretManagerSecret.Values), 2)
   617  
   618  	var response struct{ HelmReleasesStatus []*model.HelmReleaseStatus }
   619  	query := helmReleasesStatusQuery(testClusterEdgeID)
   620  	s.NoError(GraphqlRetry(query, &response, func() bool { return len(response.HelmReleasesStatus) > 0 }))
   621  	for _, helmReleasesStatus := range response.HelmReleasesStatus {
   622  		s.NotEmpty(helmReleasesStatus.Name)
   623  		s.Empty(helmReleasesStatus.LastActionTime)
   624  		s.Empty(helmReleasesStatus.VersionInstalled)
   625  		s.NotEmpty(helmReleasesStatus.VersionRequested)
   626  		s.Empty(helmReleasesStatus.InstallCondition)
   627  		s.Empty(helmReleasesStatus.ReadyCondition)
   628  		s.Empty(helmReleasesStatus.ConfigValues)
   629  	}
   630  }
   631  
   632  func (s Suite) TestHelmCharts() {
   633  	integration.SkipIf(s.Framework)
   634  	helmURL := testHelmRepoURL
   635  	if !integration.IsIntegrationTest() {
   636  		srv := s.createHelmRepoTestServer()
   637  		helmURL = srv.URL
   638  	}
   639  
   640  	chartSecret := fmt.Sprintf("helm-chart-secret-%s", s.testTime)
   641  	var secretResponse struct{ CreateOrUpdateSecretManagerSecret bool } //nolint: dupl
   642  	mutation := createOrUpdateSecretManagerSecretMutation(testOrgBannerEdgeID, chartSecret, "edge-helm", "helm-repository", "tenant", []model.KeyValues{
   643  		{Key: "username", Value: "jd250001"},
   644  		{Key: "password", Value: "password1"},
   645  		{Key: "helmUrl", Value: helmURL},
   646  	})
   647  	ResolverClient.MustPost(mutation, &secretResponse)
   648  	s.True(secretResponse.CreateOrUpdateSecretManagerSecret)
   649  
   650  	var resp struct{ HelmCharts []*model.HelmChart }
   651  	query := helmChartsQuery(chartSecret, testOrgBannerEdgeID)
   652  	s.NoError(GraphqlRetry(query, &resp, func() bool { return len(resp.HelmCharts) > 0 }))
   653  	s.Len(resp.HelmCharts, 2)
   654  	s.NotEmpty(resp.HelmCharts[0].Name)
   655  	s.NotEmpty(resp.HelmCharts[1].Name)
   656  }
   657  
   658  func (s Suite) TestHelmRepositoryInfo() { //nolint: dupl
   659  	integration.SkipIf(s.Framework)
   660  	helmURL := testHelmRepoURL
   661  	if !integration.IsIntegrationTest() {
   662  		srv := s.createHelmRepoTestServer()
   663  		helmURL = srv.URL
   664  	}
   665  
   666  	chartSecret := fmt.Sprintf("helmrepo-info-secret-%s", s.testTime)
   667  	var secretResponse struct{ CreateOrUpdateSecretManagerSecret bool }
   668  	mutation := createOrUpdateSecretManagerSecretMutation(testOrgBannerEdgeID, chartSecret, "edge-helm", "helm-repository", "tenant", []model.KeyValues{
   669  		{Key: "username", Value: "jd250001"},
   670  		{Key: "password", Value: "password1"},
   671  		{Key: "helmUrl", Value: helmURL},
   672  	})
   673  	ResolverClient.MustPost(mutation, &secretResponse)
   674  	s.True(secretResponse.CreateOrUpdateSecretManagerSecret)
   675  
   676  	var resp struct{ HelmRepositoryInfo *model.HelmRepositoryInfo }
   677  	query := helmRepositoryInfoQuery(chartSecret, testOrgBannerEdgeID, testHelmChart, testHelmVersion)
   678  	s.NoError(GraphqlRetry(query, &resp, func() bool { return resp.HelmRepositoryInfo != nil }))
   679  	s.NotNil(resp.HelmRepositoryInfo)
   680  	s.NotEmpty(resp.HelmRepositoryInfo.Readme)
   681  }
   682  
   683  func (s Suite) TestGetHelmWorkloads() {
   684  	integration.SkipIf(s.Framework)
   685  
   686  	srv := s.createHelmRepoTestServer()
   687  	helmURL := srv.URL
   688  
   689  	var secretResponse struct{ CreateOrUpdateSecretManagerSecret bool } //nolint: dupl
   690  	mutation := createOrUpdateSecretManagerSecretMutation(testOrgBannerEdgeID, "test-helm-workload", "edge-helm", "helm-repository", "tenant", []model.KeyValues{
   691  		{Key: "username", Value: "jd250001"},
   692  		{Key: "password", Value: "password1"},
   693  		{Key: "helmUrl", Value: helmURL},
   694  	})
   695  
   696  	ResolverClient.MustPost(mutation, &secretResponse)
   697  	s.True(secretResponse.CreateOrUpdateSecretManagerSecret)
   698  
   699  	var response struct{ HelmWorkloads []*model.HelmWorkload }
   700  	query := helmWorkloadsQuery(testClusterEdgeID, "")
   701  
   702  	s.NoError(GraphqlRetry(query, &response, func() bool { return len(response.HelmWorkloads) > 0 }))
   703  
   704  	responseMap := make(map[string]bool, 0)
   705  	for i := 0; i < len(response.HelmWorkloads); i++ {
   706  		if responseMap[response.HelmWorkloads[i].HelmEdgeID] {
   707  			s.Fail(fmt.Sprintf("duplicate helm release returned: %s", response.HelmWorkloads[i].Name))
   708  		} else {
   709  			responseMap[response.HelmWorkloads[i].HelmEdgeID] = true
   710  		}
   711  	}
   712  
   713  	s.NotNil(response.HelmWorkloads)
   714  	s.GreaterOrEqual(len(response.HelmWorkloads), 1)
   715  	s.Equal(testClusterEdgeID, response.HelmWorkloads[0].ClusterEdgeID)
   716  	s.NotEmpty(response.HelmWorkloads[0].Name)
   717  	for _, helmWorkloadVal := range response.HelmWorkloads {
   718  		s.NotEmpty(helmWorkloadVal.HelmEdgeID)
   719  		s.NotEmpty(helmWorkloadVal.HelmChart)
   720  		s.NotEmpty(helmWorkloadVal.HelmChartVersion)
   721  		s.NotEmpty(helmWorkloadVal.HelmRepoSecret)
   722  		s.NotEmpty(helmWorkloadVal.HelmRepository)
   723  		s.NotEmpty(helmWorkloadVal.Namespace)
   724  		s.NotEmpty(helmWorkloadVal.InstalledBy)
   725  		s.Empty(*helmWorkloadVal.UpdateAvailable)
   726  		s.NotEmpty(helmWorkloadVal.UpdatedAt)
   727  		s.NotEmpty(helmWorkloadVal.CreatedAt)
   728  		s.Equal(helmWorkloadVal.Labels, []*model.Label(nil))
   729  	}
   730  
   731  	// Test getting workloads by bannerEdgeID and verify that deployed workloads
   732  	// have a clusterEdgeID and undeployed workloads don't.
   733  	undeployedHelmEdgeID := "930c89f4-060b-456e-a779-7cacb50c3bec"
   734  	deployedHelmEdgeID := testHelmEdgeID
   735  	query = helmWorkloadsQuery("", testOrgBannerEdgeID)
   736  	s.NoError(ResolverClient.Post(query, &response))
   737  	s.NotEmpty(response.HelmWorkloads)
   738  
   739  	for _, workload := range response.HelmWorkloads {
   740  		if workload.HelmEdgeID == undeployedHelmEdgeID {
   741  			s.Empty(workload.ClusterEdgeID)
   742  		} else if workload.HelmEdgeID == deployedHelmEdgeID {
   743  			s.Equal(testClusterEdgeID, workload.ClusterEdgeID)
   744  		}
   745  		s.Equal(testOrgBannerEdgeID, workload.BannerEdgeID)
   746  		s.NotEmpty(workload.Name)
   747  		s.NotEmpty(workload.HelmEdgeID)
   748  		s.NotEmpty(workload.HelmChart)
   749  		s.NotEmpty(workload.HelmChartVersion)
   750  		s.NotEmpty(workload.HelmRepoSecret)
   751  		s.NotEmpty(workload.HelmRepository)
   752  		s.NotEmpty(workload.Namespace)
   753  		s.NotEmpty(workload.InstalledBy)
   754  		s.Empty(*workload.UpdateAvailable)
   755  		s.NotEmpty(workload.UpdatedAt)
   756  		s.NotEmpty(workload.CreatedAt)
   757  		s.Equal(workload.Labels, []*model.Label(nil))
   758  	}
   759  }
   760  
   761  func (s Suite) TestGetHelmWorkload() {
   762  	integration.SkipIf(s.Framework)
   763  
   764  	srv := s.createHelmRepoTestServer()
   765  	helmURL := srv.URL
   766  
   767  	var secretResponse struct{ CreateOrUpdateSecretManagerSecret bool } //nolint: dupl
   768  	mutation := createOrUpdateSecretManagerSecretMutation(testOrgBannerEdgeID, "test-helm-workload", "edge-helm", "helm-repository", "tenant", []model.KeyValues{
   769  		{Key: "username", Value: "jd250001"},
   770  		{Key: "password", Value: "password1"},
   771  		{Key: "helmUrl", Value: helmURL},
   772  	})
   773  
   774  	s.NoError(ResolverClient.Post(mutation, &secretResponse))
   775  	s.True(secretResponse.CreateOrUpdateSecretManagerSecret)
   776  
   777  	var response struct{ HelmWorkload *model.HelmWorkload }
   778  	query := helmWorkloadQuery(testClusterEdgeID, testHelmEdgeID)
   779  	s.NoError(ResolverClient.Post(query, &response))
   780  
   781  	s.NotNil(response.HelmWorkload)
   782  	s.NotEmpty(response.HelmWorkload.Name)
   783  	s.Equal(response.HelmWorkload.Name, "test-helm-workload")
   784  	s.NotEmpty(response.HelmWorkload.HelmEdgeID)
   785  	s.NotEmpty(response.HelmWorkload.HelmChart)
   786  	s.Equal(testOrgBannerEdgeID, response.HelmWorkload.BannerEdgeID)
   787  	s.Equal(testClusterEdgeID, response.HelmWorkload.ClusterEdgeID)
   788  	s.NotEmpty(response.HelmWorkload.HelmChartVersion)
   789  	s.NotEmpty(response.HelmWorkload.HelmRepoSecret)
   790  	s.NotEmpty(response.HelmWorkload.HelmRepository)
   791  	s.NotEmpty(response.HelmWorkload.Namespace)
   792  	s.NotEmpty(response.HelmWorkload.InstalledBy)
   793  	s.Empty(*response.HelmWorkload.UpdateAvailable)
   794  	s.NotEmpty(response.HelmWorkload.UpdatedAt)
   795  	s.NotEmpty(response.HelmWorkload.CreatedAt)
   796  	s.Equal(response.HelmWorkload.UpgradeableVersions, []*model.HelmVersion(nil))
   797  	s.Equal(response.HelmWorkload.DowngradeableVersions, []*model.HelmVersion(nil))
   798  	s.Equal(len(response.HelmWorkload.Secrets), 2)
   799  	s.NotEmpty(response.HelmWorkload.Labels)
   800  }
   801  
   802  func (s Suite) TestAddWorkloadLabels() {
   803  	integration.SkipIf(s.Framework)
   804  
   805  	labelIDList := []string{"ca18bfab-091b-4a7f-9db6-011d2603b949", "bbb5a40b-65d1-4092-ab5f-e35d7c2482db"}
   806  	invalidabelIDList := []string{"ca18bfabCHIPPY2603b949", "442SCOOBYDOO458b7536"}
   807  
   808  	// Case 1: Adding valid label
   809  	var response1 struct {
   810  		AddWorkloadLabels bool
   811  	}
   812  	mutation1 := addWorkloadLabelsMutation(testHelmEdgeID, labelIDList)
   813  	ResolverClient.MustPost(mutation1, &response1)
   814  	s.NotNil(response1)
   815  	s.Equal(true, response1.AddWorkloadLabels)
   816  
   817  	// Case 2: Adding invalid label
   818  	var response2 struct {
   819  		AddWorkloadLabels bool
   820  	}
   821  	mutation2 := addWorkloadLabelsMutation(testHelmEdgeID, invalidabelIDList)
   822  	err2 := ResolverClient.Post(mutation2, &response2)
   823  	s.Equal(false, response2.AddWorkloadLabels)
   824  	s.Contains(err2.Error(), "[{\"message\":\"Invalid input syntax for type\",\"extensions\":{\"additional\":{\"errorType\":\"EDGE_SQL_STATE\",\"severity\":\"\",\"statusCode\":\"Unknown\"}")
   825  }
   826  
   827  func (s Suite) TestDeleteWorkloadLabels() {
   828  	integration.SkipIf(s.Framework)
   829  
   830  	helmEdgeID := "be8536ff-d463-4aff-8fa9-fe81fec1ddc2"
   831  	labelEdgeID := "aac1f183-50bc-453f-a822-9ab11aa70916"
   832  	invalidLabel := "jibberish"
   833  	deleteLabel := "bbb5a40b-65d1-4092-ab5f-e35d7c2482dc"
   834  
   835  	workloadLabelInput := model.WorkloadLabelInput{
   836  		HelmEdgeID:  helmEdgeID,
   837  		LabelEdgeID: labelEdgeID,
   838  	}
   839  
   840  	// Case 1: Deleting valid label
   841  	var response1 struct {
   842  		DeleteWorkloadLabel bool
   843  	}
   844  	mutation1 := deleteWorkloadLabelMutation(workloadLabelInput.HelmEdgeID, workloadLabelInput.LabelEdgeID)
   845  	ResolverClient.MustPost(mutation1, &response1)
   846  	s.NotNil(response1)
   847  
   848  	// Case 2: Deleting invalid label
   849  	var response2 struct {
   850  		DeleteWorkloadLabel bool
   851  	}
   852  	mutation2 := deleteWorkloadLabelMutation(workloadLabelInput.HelmEdgeID, invalidLabel)
   853  	err2 := ResolverClient.Post(mutation2, &response2)
   854  	s.Equal(false, response2.DeleteWorkloadLabel)
   855  	s.Contains(err2.Error(), "[{\"message\":\"failed to delete labels from workload\",\"path\":[\"deleteWorkloadLabel\"]}]")
   856  
   857  	// Case 3: Deleting empty label
   858  	var response3 struct {
   859  		DeleteWorkloadLabel bool
   860  	}
   861  	mutation3 := deleteWorkloadLabelMutation(workloadLabelInput.HelmEdgeID, "")
   862  	ResolverClient.MustPost(mutation3, &response3)
   863  	s.NotNil(response3)
   864  	s.Equal(true, response3.DeleteWorkloadLabel)
   865  
   866  	// Case 4: Deleting with label edge id
   867  	var response4 struct {
   868  		DeleteWorkloadLabel bool
   869  	}
   870  	mutation4 := deleteWorkloadLabelMutation("", deleteLabel)
   871  	ResolverClient.MustPost(mutation4, &response4)
   872  	s.NotNil(response4)
   873  	s.Equal(true, response4.DeleteWorkloadLabel)
   874  }
   875  
   876  func (s Suite) createHelmRepoTestServer() *httptest.Server {
   877  	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   878  		var err error
   879  		w.WriteHeader(200)
   880  		if strings.HasSuffix(r.URL.String(), "/README.md") {
   881  			_, err = w.Write([]byte(readme))
   882  		} else if strings.HasSuffix(r.URL.String(), "/values.schema.json") {
   883  			_, err = w.Write([]byte(schemaData))
   884  		} else if strings.HasSuffix(r.URL.String(), ".tgz") {
   885  			_, err = w.Write([]byte(zippedChart))
   886  			w.Header().Set("Content-Type", "application/gzip")
   887  			w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.tgz\"", "alpine-0.2.0.tgz"))
   888  		} else {
   889  			_, err = w.Write([]byte(getTestHelmRepoIndex(helmCharts, r.Host)))
   890  		}
   891  		s.NoError(err)
   892  	}))
   893  }
   894  
   895  func getTestHelmRepoIndex(helmCharts, url string) string {
   896  	return fmt.Sprintf(helmCharts, url, url)
   897  }
   898  
   899  func createTestHelmRepoSecret(bannerEdgeID, name, helmURL string) error {
   900  	var createResponse struct{ CreateOrUpdateSecretManagerSecret bool }
   901  	mutation := createOrUpdateSecretManagerSecretMutation(bannerEdgeID, name, "edge-helm", "helm-repository", "tenant", []model.KeyValues{
   902  		{Key: "helm_repo_name", Value: name},
   903  		{Key: "helmUrl", Value: helmURL},
   904  	})
   905  	ResolverClient.MustPost(mutation, &createResponse)
   906  	mutation = createOrUpdateBannerHelmRepositoryMutation(bannerEdgeID, name, testHelmRepoURL, name)
   907  	var createUpdateRepositoryResponse struct{ CreateOrUpdateBannerHelmRepository bool }
   908  	ResolverClient.MustPost(mutation, &createUpdateRepositoryResponse)
   909  	if !createUpdateRepositoryResponse.CreateOrUpdateBannerHelmRepository {
   910  		return fmt.Errorf("unable to create or update HelmRepository")
   911  	}
   912  
   913  	return nil
   914  }
   915  
   916  func (s *Suite) createHelmReleaseMutation(bannerEdgeID, clusterEdgeID, name, namespace, secret, helmRepository, helmChart, version, configValues string, helmEdgeID string, secrets ...string) string { //nolint: unparam
   917  	payloadArgs := []graphb.Argument{
   918  		graphb.ArgumentString("name", name),
   919  		graphb.ArgumentString("secret", secret),
   920  		graphb.ArgumentString("helmRepository", helmRepository),
   921  		graphb.ArgumentString("helmChart", helmChart),
   922  		graphb.ArgumentString("version", version),
   923  		graphb.ArgumentString("configValues", ""),
   924  	}
   925  
   926  	if bannerEdgeID != "" {
   927  		payloadArgs = append(payloadArgs, graphb.ArgumentString("bannerEdgeId", bannerEdgeID))
   928  	}
   929  	if clusterEdgeID != "" {
   930  		payloadArgs = append(payloadArgs, graphb.ArgumentString("clusterEdgeId", clusterEdgeID))
   931  	}
   932  	if len(secrets) > 0 {
   933  		payloadArgs = append(payloadArgs, graphb.ArgumentStringSlice("secrets", secrets...))
   934  	}
   935  	if namespace != "" {
   936  		payloadArgs = append(payloadArgs, graphb.ArgumentString("namespace", namespace))
   937  	}
   938  	if configValues != "" {
   939  		payloadArgs = append(payloadArgs, graphb.ArgumentString("configValues", configValues))
   940  	}
   941  	if !integration.IsIntegrationTest() {
   942  		var installationType model.WorkloadInstallationType
   943  		obj, err := mapper.ToCreateHelmRelease(name, helmRepository, helmChart, version, secret, namespace, nil, installationType, nil, "0.21", helmEdgeID)
   944  		s.NoError(err)
   945  		s.NoError(createHelmReleaseResources(obj.(*helmApi.HelmRelease)))
   946  	}
   947  	return MustParse(graphb.Query{
   948  		Type: graphb.TypeMutation,
   949  		Fields: []*graphb.Field{
   950  			{
   951  				Name:      "createHelmRelease",
   952  				Arguments: []graphb.Argument{graphb.ArgumentCustomType("payload", payloadArgs...)},
   953  			},
   954  		},
   955  	})
   956  }
   957  
   958  func createOrUpdateBannerHelmRepositoryMutation(bannerEdgeID, name, url, secret string) string {
   959  	args := []graphb.Argument{
   960  		graphb.ArgumentString("bannerEdgeId", bannerEdgeID),
   961  		graphb.ArgumentString("name", name),
   962  		graphb.ArgumentString("url", url),
   963  	}
   964  	if secret != "" {
   965  		args = append(args, graphb.ArgumentString("secret", secret))
   966  	}
   967  	return MustParse(graphb.Query{
   968  		Type: graphb.TypeMutation,
   969  		Fields: []*graphb.Field{
   970  			{
   971  				Name:      "createOrUpdateBannerHelmRepository",
   972  				Arguments: args,
   973  			},
   974  		},
   975  	})
   976  }
   977  
   978  func helmChartsQuery(secretName, bannerEdgeID string) string {
   979  	return MustParse(graphb.Query{
   980  		Type: graphb.TypeQuery,
   981  		Fields: []*graphb.Field{
   982  			{
   983  				Name: "helmCharts",
   984  				Arguments: []graphb.Argument{
   985  					graphb.ArgumentString("secretName", secretName),
   986  					graphb.ArgumentString("bannerEdgeId", bannerEdgeID),
   987  				},
   988  				Fields: graphb.Fields("name", "description", "version", "appVersion", "icon", "keywords", "sources", "urls", "created"),
   989  			},
   990  		},
   991  	})
   992  }
   993  
   994  func defaultSchemaConfigQuery(secretName, bannerEdgeID, chartName, chartVersion string) string {
   995  	paramsArgs := []graphb.Argument{
   996  		graphb.ArgumentString("bannerEdgeId", bannerEdgeID),
   997  		graphb.ArgumentString("secretName", secretName),
   998  		graphb.ArgumentString("chartName", chartName),
   999  		graphb.ArgumentString("chartVersion", chartVersion),
  1000  	}
  1001  	return MustParse(graphb.Query{
  1002  		Type: graphb.TypeQuery,
  1003  		Fields: []*graphb.Field{
  1004  			{
  1005  				Name:      "defaultSchemaConfig",
  1006  				Arguments: []graphb.Argument{graphb.ArgumentCustomType("params", paramsArgs...)},
  1007  				Fields:    graphb.Fields("configVals", "configSchema"),
  1008  			},
  1009  		},
  1010  	})
  1011  }
  1012  
  1013  func helmWorkloadsQuery(clusterEdgeID, bannerEdgeID string) string {
  1014  	return MustParse(graphb.Query{
  1015  		Type: graphb.TypeQuery,
  1016  		Fields: []*graphb.Field{
  1017  			{
  1018  				Name: "helmWorkloads",
  1019  				Arguments: []graphb.Argument{
  1020  					graphb.ArgumentString("clusterEdgeId", clusterEdgeID),
  1021  					graphb.ArgumentString("bannerEdgeId", bannerEdgeID),
  1022  				},
  1023  				Fields: []*graphb.Field{
  1024  					graphb.NewField("helmChart"),
  1025  					graphb.NewField("helmChartVersion"),
  1026  					graphb.NewField("helmEdgeID"),
  1027  					graphb.NewField("clusterEdgeID"),
  1028  					graphb.NewField("bannerEdgeID"),
  1029  					graphb.NewField("helmRepoSecret"),
  1030  					graphb.NewField("helmRepository"),
  1031  					graphb.NewField("installedBy"),
  1032  					graphb.NewField("name"),
  1033  					graphb.NewField("namespace"),
  1034  					graphb.NewField("installationType"),
  1035  					graphb.NewField("updateAvailable"),
  1036  					graphb.NewField("configValues"),
  1037  					graphb.NewField("createdAt"),
  1038  					graphb.NewField("updatedAt"),
  1039  					{
  1040  						Name:   "secrets",
  1041  						Fields: graphb.Fields("createdAt", "name", "secretEdgeID", "updatedAt"),
  1042  					},
  1043  					{
  1044  						Name:   "upgradeableVersions",
  1045  						Fields: graphb.Fields("version"),
  1046  					},
  1047  					{
  1048  						Name:   "downgradeableVersions",
  1049  						Fields: graphb.Fields("version"),
  1050  					},
  1051  				},
  1052  			},
  1053  		},
  1054  	})
  1055  }
  1056  
  1057  func helmWorkloadQuery(clusterEdgeID string, helmEdgeID string) string {
  1058  	return MustParse(graphb.Query{
  1059  		Type: graphb.TypeQuery,
  1060  		Fields: []*graphb.Field{
  1061  			{
  1062  				Name: "helmWorkload",
  1063  				Arguments: []graphb.Argument{
  1064  					graphb.ArgumentString("clusterEdgeId", clusterEdgeID),
  1065  					graphb.ArgumentString("helmEdgeId", helmEdgeID),
  1066  				},
  1067  				Fields: []*graphb.Field{
  1068  					graphb.NewField("bannerEdgeID"),
  1069  					graphb.NewField("clusterEdgeID"),
  1070  					graphb.NewField("helmChart"),
  1071  					graphb.NewField("helmChartVersion"),
  1072  					graphb.NewField("helmEdgeID"),
  1073  					graphb.NewField("helmRepoSecret"),
  1074  					graphb.NewField("helmRepository"),
  1075  					graphb.NewField("installedBy"),
  1076  					graphb.NewField("name"),
  1077  					graphb.NewField("namespace"),
  1078  					graphb.NewField("installationType"),
  1079  					graphb.NewField("updateAvailable"),
  1080  					graphb.NewField("configValues"),
  1081  					graphb.NewField("createdAt"),
  1082  					graphb.NewField("updatedAt"),
  1083  					{
  1084  						Name:   "secrets",
  1085  						Fields: graphb.Fields("createdAt", "name", "secretEdgeID", "updatedAt"),
  1086  					},
  1087  					{
  1088  						Name:   "upgradeableVersions",
  1089  						Fields: graphb.Fields("version"),
  1090  					},
  1091  					{
  1092  						Name:   "downgradeableVersions",
  1093  						Fields: graphb.Fields("version"),
  1094  					},
  1095  					{
  1096  						Name:   "labels",
  1097  						Fields: graphb.Fields("labelEdgeId", "key", "color", "visible", "editable", "unique", "bannerEdgeId", "description", "type"),
  1098  					},
  1099  				},
  1100  			},
  1101  		},
  1102  	})
  1103  }
  1104  
  1105  func helmRepositoryInfoQuery(secretName, bannerEdgeID, chartName, chartVersion string) string {
  1106  	paramsArgs := []graphb.Argument{
  1107  		graphb.ArgumentString("bannerEdgeId", bannerEdgeID),
  1108  		graphb.ArgumentString("secretName", secretName),
  1109  		graphb.ArgumentString("chartName", chartName),
  1110  		graphb.ArgumentString("chartVersion", chartVersion),
  1111  	}
  1112  	return MustParse(graphb.Query{
  1113  		Type: graphb.TypeQuery,
  1114  		Fields: []*graphb.Field{
  1115  			{
  1116  				Name:      "helmRepositoryInfo",
  1117  				Arguments: []graphb.Argument{graphb.ArgumentCustomType("params", paramsArgs...)},
  1118  				Fields:    graphb.Fields("readme", "metadata"),
  1119  			},
  1120  		},
  1121  	})
  1122  }
  1123  
  1124  func helmReleasesStatusQuery(clusterEdgeID string) string {
  1125  	return MustParse(graphb.Query{
  1126  		Type: graphb.TypeQuery,
  1127  		Fields: []*graphb.Field{
  1128  			{
  1129  				Name: "helmReleasesStatus",
  1130  				Arguments: []graphb.Argument{
  1131  					graphb.ArgumentString("clusterEdgeId", clusterEdgeID),
  1132  				},
  1133  				Fields: []*graphb.Field{
  1134  					graphb.NewField("configValues"),
  1135  					graphb.NewField("lastActionTime"),
  1136  					graphb.NewField("name"),
  1137  					graphb.NewField("statusType"),
  1138  					graphb.NewField("versionInstalled"),
  1139  					graphb.NewField("versionRequested"),
  1140  					{
  1141  						Name:   "installCondition",
  1142  						Fields: graphb.Fields("installed", "lastTransitionTime", "ready", "message", "reason", "status", "type"),
  1143  					},
  1144  					{
  1145  						Name:   "readyCondition",
  1146  						Fields: graphb.Fields("installed", "lastTransitionTime", "message", "ready", "reason", "status", "type"),
  1147  					},
  1148  				},
  1149  			},
  1150  		},
  1151  	})
  1152  }
  1153  
  1154  func helmChartVersionQuery(name, secretName, bannerEdgeID string) string {
  1155  	return MustParse(graphb.Query{
  1156  		Type: graphb.TypeQuery,
  1157  		Fields: []*graphb.Field{
  1158  			{
  1159  				Name: "helmChartVersion",
  1160  				Arguments: []graphb.Argument{
  1161  					graphb.ArgumentString("name", name),
  1162  					graphb.ArgumentString("secretName", secretName),
  1163  					graphb.ArgumentString("bannerEdgeId", bannerEdgeID),
  1164  				},
  1165  				Fields: graphb.Fields("name", "versions"),
  1166  			},
  1167  		},
  1168  	})
  1169  }
  1170  
  1171  func updateHelmReleaseMutation(helmEdgeID, clusterEdgeID, helmReleaseName, version, configValues string, secrets, labelEdgeIDs []string) string {
  1172  	args := []graphb.Argument{
  1173  		graphb.ArgumentString("helmEdgeId", helmEdgeID),
  1174  		graphb.ArgumentString("clusterEdgeId", clusterEdgeID),
  1175  		graphb.ArgumentString("helmReleaseName", helmReleaseName),
  1176  	}
  1177  	if version != "" {
  1178  		args = append(args, graphb.ArgumentString("version", version))
  1179  	}
  1180  	if configValues != "" {
  1181  		args = append(args, graphb.ArgumentString("configValues", configValues))
  1182  	}
  1183  	if secrets != nil {
  1184  		args = append(args, graphb.ArgumentStringSlice("secrets", secrets...))
  1185  	}
  1186  	if labelEdgeIDs != nil {
  1187  		args = append(args, graphb.ArgumentStringSlice("labelEdgeIds", labelEdgeIDs...))
  1188  	}
  1189  
  1190  	return MustParse(graphb.Query{
  1191  		Type: graphb.TypeMutation,
  1192  		Fields: []*graphb.Field{
  1193  			{
  1194  				Name:      "updateHelmRelease",
  1195  				Arguments: args,
  1196  			},
  1197  		},
  1198  	})
  1199  }
  1200  
  1201  func deleteHelmReleaseMutation(helmEdgeID, clusterEdgeID, name string) string {
  1202  	var args = []graphb.Argument{
  1203  		graphb.ArgumentString("helmEdgeId", helmEdgeID),
  1204  		graphb.ArgumentString("clusterEdgeId", clusterEdgeID),
  1205  		graphb.ArgumentString("name", name),
  1206  	}
  1207  	return MustParse(graphb.Query{
  1208  		Type: graphb.TypeMutation,
  1209  		Fields: []*graphb.Field{
  1210  			{
  1211  				Name:      "deleteHelmRelease",
  1212  				Arguments: args,
  1213  			},
  1214  		},
  1215  	})
  1216  }
  1217  
  1218  // createHelmReleaseResources NOTE: any time we create HelmRelease we should create the resources
  1219  func createHelmReleaseResources(hr *helmApi.HelmRelease) error {
  1220  	namespace := hr.Spec.TargetNamespace
  1221  	name := hr.Name
  1222  	httpPort := intstr.FromInt(8000)
  1223  	dep := mapper.GetTestWorkloadDeployment(httpPort)
  1224  	ss := mapper.GetTestWorkloadStatefulSet(httpPort)
  1225  	ds := mapper.GetTestWorkloadDaemonSet(httpPort)
  1226  
  1227  	releaseName := fmt.Sprintf("%s-%s", namespace, name)
  1228  	dep.Namespace = namespace
  1229  	dep.Name = name
  1230  	dep.Annotations["meta.helm.sh/release-name"] = releaseName
  1231  
  1232  	ss.Namespace = namespace
  1233  	ss.Name = name
  1234  	ss.Annotations["meta.helm.sh/release-name"] = releaseName
  1235  
  1236  	ds.Namespace = namespace
  1237  	ds.Name = name
  1238  	ds.Annotations["meta.helm.sh/release-name"] = releaseName
  1239  
  1240  	if err := runtimeClient.Create(context.Background(), dep); err != nil {
  1241  		return err
  1242  	}
  1243  	if err := runtimeClient.Create(context.Background(), ss); err != nil {
  1244  		return err
  1245  	}
  1246  	return runtimeClient.Create(context.Background(), ds)
  1247  }
  1248  
  1249  func addWorkloadLabelsMutation(helmEdgeID string, labelEdgeIDs []string) string {
  1250  	args := []graphb.Argument{
  1251  		graphb.ArgumentString("helmEdgeId", helmEdgeID),
  1252  	}
  1253  	if labelEdgeIDs != nil {
  1254  		args = append(args, graphb.ArgumentStringSlice("labelEdgeIds", labelEdgeIDs...))
  1255  	}
  1256  	return MustParse(graphb.Query{
  1257  		Type: graphb.TypeMutation,
  1258  		Fields: []*graphb.Field{
  1259  			{
  1260  				Name:      "addWorkloadLabels",
  1261  				Arguments: args,
  1262  			},
  1263  		},
  1264  	})
  1265  }
  1266  
  1267  func deleteWorkloadLabelMutation(helmEdgeID string, labelEdgeID string) string {
  1268  	workloadLabelParams := graphb.ArgumentCustomTypeSliceElem(
  1269  		graphb.ArgumentString("helmEdgeId", helmEdgeID),
  1270  		graphb.ArgumentString("labelEdgeId", labelEdgeID),
  1271  	)
  1272  	args := []graphb.Argument{
  1273  		graphb.ArgumentCustomType("workloadLabelParameters", workloadLabelParams...),
  1274  	}
  1275  
  1276  	return MustParse(graphb.Query{
  1277  		Type: graphb.TypeMutation,
  1278  		Fields: []*graphb.Field{
  1279  			{
  1280  				Name:      "deleteWorkloadLabel",
  1281  				Arguments: args,
  1282  			},
  1283  		},
  1284  	})
  1285  }
  1286  
  1287  // Note: this function does not reliably fetch a workload's helmEdgeID if there are multiple
  1288  // workloads with the same name under the same banner or cluster. Use with caution.
  1289  func (s Suite) getHelmEdgeIDByNameAndID(ctx context.Context, helmReleaseName string, clusterEdgeID, bannerEdgeID *string) (string, error) {
  1290  	var (
  1291  		helmEdgeID string
  1292  		row        *sql.Row
  1293  	)
  1294  
  1295  	if !utils.IsNullOrEmpty(bannerEdgeID) {
  1296  		row = s.DB.QueryRowContext(ctx, sqlquery.GetHelmEdgeIDByNameAndBannerEdgeID, helmReleaseName, bannerEdgeID)
  1297  	} else if !utils.IsNullOrEmpty(clusterEdgeID) {
  1298  		row = s.DB.QueryRowContext(ctx, sqlquery.GetHelmEdgeIDByNameAndClusterEdgeID, helmReleaseName, clusterEdgeID)
  1299  	} else {
  1300  		return "", apierror.New("please provide clusterEdgeId or bannerEdgeId")
  1301  	}
  1302  
  1303  	if err := row.Scan(&helmEdgeID); err != nil {
  1304  		return "", fmt.Errorf("failed to get helmEdgeID for workload: %w", err)
  1305  	}
  1306  	return helmEdgeID, nil
  1307  }
  1308  

View as plain text