github.com/verrazzano/verrazzano@v1.7.1/tests/e2e/upgrade/post-upgrade/verify/verify_restart_test.go (about) 1 // Copyright (c) 2021, 2023, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package verify 5 6 import ( 7 "fmt" 8 cmconstants "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/certmanager/constants" 9 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/opensearch" 10 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/opensearchdashboards" 11 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/opensearchoperator" 12 "strings" 13 "time" 14 15 vzconst "github.com/verrazzano/verrazzano/pkg/constants" 16 "github.com/verrazzano/verrazzano/pkg/helm" 17 "github.com/verrazzano/verrazzano/pkg/k8sutil" 18 "github.com/verrazzano/verrazzano/pkg/log/vzlog" 19 "github.com/verrazzano/verrazzano/pkg/nginxutil" 20 vzapi "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1alpha1" 21 "github.com/verrazzano/verrazzano/platform-operator/constants" 22 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/appoper" 23 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/authproxy" 24 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/coherence" 25 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/externaldns" 26 compistio "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/istio" 27 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/keycloak" 28 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/kiali" 29 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/mysql" 30 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/nginx" 31 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/oam" 32 promoperator "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/prometheus/operator" 33 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/rancher" 34 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/verrazzano" 35 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/weblogic" 36 "github.com/verrazzano/verrazzano/tests/e2e/pkg" 37 "github.com/verrazzano/verrazzano/tests/e2e/pkg/test/framework" 38 "k8s.io/apimachinery/pkg/api/errors" 39 40 . "github.com/onsi/ginkgo/v2" 41 . "github.com/onsi/gomega" 42 ) 43 44 const ( 45 shortWait = 1 * time.Minute 46 mediumWait = 2 * time.Minute 47 longWait = 5 * time.Minute 48 49 pollingInterval = 10 * time.Second 50 minimumVersion = "1.1.0" 51 ) 52 53 var t = framework.NewTestFramework("verify") 54 55 var vzcr *vzapi.Verrazzano 56 var kubeconfigPath string 57 var envoyImage string 58 var beforeSuite = t.BeforeSuiteFunc(func() { 59 var err error 60 kubeconfigPath, err = k8sutil.GetKubeConfigLocation() 61 if err != nil { 62 Fail(fmt.Sprintf("Failed to get default kubeconfig path: %s", err.Error())) 63 } 64 65 // Get the Verrazzano CR 66 Eventually(func() error { 67 var err error 68 vzcr, err = pkg.GetVerrazzano() 69 if err != nil { 70 return err 71 } 72 return err 73 }, shortWait, pollingInterval).Should(BeNil(), "Expected to get Verrazzano CR") 74 // Get the envoy proxy image name and tag 75 Eventually(func() error { 76 var err error 77 envoyImage, err = getEnvoyProxyImageRef() 78 if err != nil { 79 return err 80 } 81 return err 82 }, shortWait, pollingInterval).Should(BeNil(), "Expected to get envoy proxy image name and tag") 83 }) 84 var _ = BeforeSuite(beforeSuite) 85 var afterSuite = t.AfterSuiteFunc(func() {}) 86 var _ = AfterSuite(afterSuite) 87 var _ = t.AfterEach(func() {}) 88 89 var _ = t.Describe("Post upgrade", Label("f:platform-lcm.upgrade"), func() { 90 91 // GIVEN the verrazzano-system namespace 92 // WHEN the container images are retrieved 93 // THEN verify that each pod that uses istio has the correct istio proxy image 94 t.ItMinimumVersion("pods in verrazzano-system have correct istio proxy image", minimumVersion, kubeconfigPath, func() { 95 Eventually(func() bool { 96 return pkg.CheckPodsForEnvoySidecar(constants.VerrazzanoSystemNamespace, envoyImage) 97 }, longWait, pollingInterval).Should(BeTrue(), "Expected to find istio proxy image in verrazzano-system") 98 }) 99 100 // GIVEN the ingress-nginx namespace 101 // WHEN the container images are retrieved 102 // THEN verify that each pod that uses istio has the correct istio proxy image 103 t.ItMinimumVersion("pods in ingress-nginx have correct istio proxy image", minimumVersion, kubeconfigPath, func() { 104 Eventually(func() bool { 105 return pkg.CheckPodsForEnvoySidecar(constants.IngressNginxNamespace, envoyImage) 106 }, longWait, pollingInterval).Should(BeTrue(), "Expected to find istio proxy image in ingress-nginx") 107 }) 108 109 // GIVEN the keycloak namespace 110 // WHEN the container images are retrieved 111 // THEN verify that each pod that uses istio has the correct istio proxy image 112 t.ItMinimumVersion("pods in keycloak have correct istio proxy image", minimumVersion, kubeconfigPath, func() { 113 Eventually(func() bool { 114 return pkg.CheckPodsForEnvoySidecar(constants.KeycloakNamespace, envoyImage) 115 }, longWait, pollingInterval).Should(BeTrue(), "Expected to find istio proxy image in keycloak") 116 }) 117 }) 118 119 var _ = t.Describe("Application pods post-upgrade", Label("f:platform-lcm.upgrade"), func() { 120 const ( 121 bobsBooksNamespace = "bobs-books" 122 helloHelidonNamespace = "hello-helidon" 123 springbootNamespace = "springboot" 124 todoListNamespace = "todo-list" 125 ) 126 t.DescribeTable("should contain Envoy sidecar with proxyv2 image", 127 func(namespace string, timeout time.Duration) { 128 exists, err := pkg.DoesNamespaceExist(namespace) 129 if err != nil { 130 Fail(err.Error()) 131 } 132 if exists { 133 Eventually(func() bool { 134 return pkg.CheckPodsForEnvoySidecar(namespace, envoyImage) 135 }, timeout, pollingInterval).Should(BeTrue(), fmt.Sprintf("Expected to find envoy sidecar %s in %s namespace", envoyImage, namespace)) 136 } else { 137 t.Logs.Infof("Skipping test since namespace %s doesn't exist", namespace) 138 } 139 }, 140 t.Entry(fmt.Sprintf("pods in namespace %s have Envoy sidecar", helloHelidonNamespace), helloHelidonNamespace, mediumWait), 141 t.Entry(fmt.Sprintf("pods in namespace %s have Envoy sidecar", springbootNamespace), springbootNamespace, mediumWait), 142 t.Entry(fmt.Sprintf("pods in namespace %s have Envoy sidecar", todoListNamespace), todoListNamespace, longWait), 143 t.Entry(fmt.Sprintf("pods in namespace %s have Envoy sidecar", bobsBooksNamespace), bobsBooksNamespace, longWait), 144 ) 145 }) 146 147 // Istio no longer uses Helm for 1.10, so make sure all the Helm releases have been deleted 148 var _ = t.Describe("Istio helm releases", Label("f:platform-lcm.upgrade"), func() { 149 const ( 150 istiod = "istiod" 151 istioBase = "istio" 152 istioIngress = "istio-ingress" 153 istioEgress = "istio-egress" 154 istioCoreDNS = "istiocoredns" 155 ) 156 t.DescribeTable("should be removed from the istio-system namespace post upgrade", 157 func(release string) { 158 Eventually(func() bool { 159 installed, _ := helm.IsReleaseInstalled(release, constants.IstioSystemNamespace) 160 return installed 161 }, mediumWait, pollingInterval).Should(BeFalse(), fmt.Sprintf("Expected to not find release %s in istio-system", release)) 162 }, 163 t.Entry(fmt.Sprintf("istio-system doesn't contain release %s", istiod), istiod), 164 t.Entry(fmt.Sprintf("istio-system doesn't contain release %s", istioBase), istioBase), 165 t.Entry(fmt.Sprintf("istio-system doesn't contain release %s", istioIngress), istioIngress), 166 t.Entry(fmt.Sprintf("istio-system doesn't contain release %s", istioEgress), istioEgress), 167 t.Entry(fmt.Sprintf("istio-system doesn't contain release %s", istioCoreDNS), istioCoreDNS), 168 ) 169 }) 170 171 var _ = t.Describe("Checking if Verrazzano system components are ready, post-upgrade", Label("f:platform-lcm.upgrade"), func() { 172 Context("Checking Deployments for post-upgrade", func() { 173 ingressNGINXNamespace, err := nginxutil.DetermineNamespaceForIngressNGINX(vzlog.DefaultLogger()) 174 if err != nil { 175 Fail(fmt.Sprintf("Failed to get Ingress NGINX namespace: %s", err.Error())) 176 } 177 t.DescribeTable("Deployment should be ready post-upgrade", 178 func(namespace string, componentName string, deploymentName string) { 179 // Currently we have no way of determining if some components are installed by looking at the status (grafana) 180 // Because of this, make this test non-managed cluster only. 181 if vzcr.Spec.Profile == vzapi.ManagedCluster { 182 return 183 } 184 Eventually(func() bool { 185 if isDisabled(componentName) { 186 t.Logs.Infof("Skipping disabled component %s", componentName) 187 return true 188 } 189 isVersionAbove1_4_0, err := pkg.IsVerrazzanoMinVersion("1.4.0", kubeconfigPath) 190 if err != nil { 191 pkg.Log(pkg.Error, fmt.Sprintf("failed to find the verrazzano version: %v", err)) 192 return false 193 } 194 isVersionAbove1_7_0, err := pkg.IsVerrazzanoMinVersion("1.7.0", kubeconfigPath) 195 if err != nil { 196 pkg.Log(pkg.Error, fmt.Sprintf("failed to find the verrazzano version: %v", err)) 197 return false 198 } 199 if !isVersionAbove1_7_0 && (deploymentName == "opensearch-operator-controller-manager" || deploymentName == "opensearch-dashboards") { 200 // skip opensearchOperator and new osd pod for version lower than 1.7.0 201 return true 202 } 203 if isVersionAbove1_7_0 && deploymentName == "vmi-system-osd" { 204 // skip legacy osd pod for version greater than 1.7.0 205 return true 206 } 207 if deploymentName == "mysql" && isVersionAbove1_4_0 { 208 // skip mysql for version greater than 1.4.0 209 return true 210 } 211 deployment, err := pkg.GetDeployment(namespace, deploymentName) 212 if err != nil { 213 return false 214 } 215 return deployment.Status.ReadyReplicas > 0 216 }, mediumWait, pollingInterval).Should(BeTrue(), fmt.Sprintf("Deployment %s for component %s is not ready", deploymentName, componentName)) 217 }, 218 t.Entry("Checking Deployment coherence-operator", constants.VerrazzanoSystemNamespace, coherence.ComponentName, "coherence-operator"), 219 t.Entry("Checking Deployment oam-kubernetes-runtime", constants.VerrazzanoSystemNamespace, oam.ComponentName, "oam-kubernetes-runtime"), 220 t.Entry("Checking Deployment verrazzano-application-operator", constants.VerrazzanoSystemNamespace, appoper.ComponentName, "verrazzano-application-operator"), 221 t.Entry("Checking Deployment verrazzano-authproxy", constants.VerrazzanoSystemNamespace, authproxy.ComponentName, "verrazzano-authproxy"), 222 t.Entry("Checking Deployment verrazzano-console", constants.VerrazzanoSystemNamespace, verrazzano.ComponentName, "verrazzano-console"), 223 t.Entry("Checking Deployment verrazzano-monitoring-operator", constants.VerrazzanoSystemNamespace, verrazzano.ComponentName, "verrazzano-monitoring-operator"), 224 t.Entry("Checking Deployment vmi-system-grafana", constants.VerrazzanoSystemNamespace, verrazzano.ComponentName, "vmi-system-grafana"), 225 t.Entry("Checking Deployment vmi-system-osd", constants.VerrazzanoSystemNamespace, verrazzano.ComponentName, "vmi-system-osd"), 226 t.Entry("Checking Deployment prometheus-operator-kube-p-operator", vzconst.PrometheusOperatorNamespace, promoperator.ComponentName, "prometheus-operator-kube-p-operator"), 227 t.Entry("Checking Deployment weblogic-operator", constants.VerrazzanoSystemNamespace, weblogic.ComponentName, "weblogic-operator"), 228 229 t.Entry("Checking Deployment opensearch-operator-controller-manager", constants.VerrazzanoLoggingNamespace, opensearchoperator.ComponentName, "opensearch-operator-controller-manager"), 230 t.Entry("Checking Deployment opensearch-dashboards", constants.VerrazzanoLoggingNamespace, opensearchdashboards.ComponentName, "opensearch-dashboards"), 231 232 t.Entry("Checking Deployment cert-manager", vzconst.CertManagerNamespace, cmconstants.CertManagerComponentName, "cert-manager"), 233 t.Entry("Checking Deployment cert-manager-cainjector", vzconst.CertManagerNamespace, cmconstants.CertManagerComponentName, "cert-manager-cainjector"), 234 t.Entry("Checking Deployment cert-manager-webhook", vzconst.CertManagerNamespace, cmconstants.CertManagerComponentName, "cert-manager-webhook"), 235 236 t.Entry("Checking Deployment external-dns", externaldns.ResolveExernalDNSNamespace(), externaldns.ComponentName, "external-dns"), 237 238 t.Entry("Checking Deployment istiod", compistio.IstioNamespace, compistio.ComponentName, "istiod"), 239 t.Entry("Checking Deployment istio-ingressgateway", compistio.IstioNamespace, compistio.ComponentName, "istio-ingressgateway"), 240 t.Entry("Checking Deployment istio-egressgateway", compistio.IstioNamespace, compistio.ComponentName, "istio-egressgateway"), 241 242 t.Entry("Checking Deployment vmi-system-kiali", constants.VerrazzanoSystemNamespace, kiali.ComponentName, "vmi-system-kiali"), 243 244 t.Entry("Checking Deployment mysql", mysql.ComponentNamespace, mysql.ComponentName, "mysql"), 245 t.Entry("Checking Deployment ingress-controller-ingress-nginx-controller", ingressNGINXNamespace, nginx.ComponentName, "ingress-controller-ingress-nginx-controller"), 246 t.Entry("Checking Deployment ingress-controller-ingress-nginx-defaultbackend", ingressNGINXNamespace, nginx.ComponentName, "ingress-controller-ingress-nginx-defaultbackend"), 247 248 t.Entry("Checking Deployment rancher", rancher.ComponentNamespace, rancher.ComponentName, "rancher"), 249 t.Entry("Checking Deployment rancher", rancher.ComponentNamespace, rancher.ComponentName, "rancher-webhook"), 250 t.Entry("Checking Deployment fleet-agent", rancher.FleetLocalSystemNamespace, rancher.ComponentName, "fleet-agent"), 251 t.Entry("Checking Deployment fleet-controller", rancher.FleetSystemNamespace, rancher.ComponentName, "fleet-controller"), 252 t.Entry("Checking Deployment gitjob", rancher.FleetSystemNamespace, rancher.ComponentName, "gitjob"), 253 ) 254 }) 255 256 Context("Checking optional Deployments for post-upgrade", func() { 257 t.DescribeTable("Deployment should be ready post-upgrade", 258 func(namespace string, componentName string, deploymentName string) { 259 // Currently we have no way of determining if some components are installed by looking at the status (grafana) 260 // Because of this, make this test non-managed cluster only. 261 if vzcr.Spec.Profile == vzapi.ManagedCluster { 262 return 263 } 264 Eventually(func() bool { 265 if isDisabled(componentName) { 266 t.Logs.Infof("Skipping disabled component %s", componentName) 267 return true 268 } 269 deployment, err := pkg.GetDeployment(namespace, deploymentName) 270 if err != nil { 271 // Deployment is optional, ignore if not found 272 // For example es-data and es-ingest won't be there for dev profile 273 if errors.IsNotFound(err) { 274 t.Logs.Infof("Skipping optional deployment %s since it is not found", deploymentName) 275 return true 276 } 277 return false 278 } 279 return deployment.Status.ReadyReplicas > 0 280 }, mediumWait, pollingInterval).Should(BeTrue(), fmt.Sprintf("Deployment %s for component %s is not ready", deploymentName, componentName)) 281 }, 282 t.Entry("Checking Deployment vmi-system-es-data-0", constants.VerrazzanoSystemNamespace, verrazzano.ComponentName, "vmi-system-es-data-0"), 283 t.Entry("Checking Deployment vmi-system-es-data-1", constants.VerrazzanoSystemNamespace, verrazzano.ComponentName, "vmi-system-es-data-1"), 284 t.Entry("Checking Deployment vmi-system-es-data-2", constants.VerrazzanoSystemNamespace, verrazzano.ComponentName, "vmi-system-es-data-2"), 285 t.Entry("Checking Deployment vmi-system-os-ingest", constants.VerrazzanoSystemNamespace, verrazzano.ComponentName, "vmi-system-os-ingest"), 286 ) 287 }) 288 289 Context("Checking StatefulSets for post-upgrade", func() { 290 t.DescribeTable("StatefulSet should be ready post-upgrade", 291 func(namespace string, componentName string, stsName string) { 292 // Currently we have no way of determining if some components are installed by looking at the status (grafana) 293 // Because of this, make this test non-managed cluster only. 294 if vzcr.Spec.Profile == vzapi.ManagedCluster { 295 return 296 } 297 Eventually(func() bool { 298 if isDisabled(componentName) { 299 t.Logs.Infof("Skipping disabled component %s", componentName) 300 return true 301 } 302 isVersionAbove1_4_0, err := pkg.IsVerrazzanoMinVersion("1.4.0", kubeconfigPath) 303 if err != nil { 304 pkg.Log(pkg.Error, fmt.Sprintf("failed to find the verrazzano version: %v", err)) 305 return false 306 } 307 isVersionAbove1_7_0, err := pkg.IsVerrazzanoMinVersion("1.7.0", kubeconfigPath) 308 if err != nil { 309 pkg.Log(pkg.Error, fmt.Sprintf("failed to find the verrazzano version: %v", err)) 310 return false 311 } 312 if !isVersionAbove1_7_0 && (stsName == "opensearch-es-master") { 313 // skip operator based os sts for version lower than 1.7.0 314 return true 315 } 316 if isVersionAbove1_7_0 && stsName == "vmi-system-es-master" { 317 // skip legacy os sts for version greater than 1.7.0 318 return true 319 } 320 if stsName == "mysql" && !isVersionAbove1_4_0 { 321 // skip mysql for version less than 1.4.0 322 return true 323 } 324 sts, err := pkg.GetStatefulSet(namespace, stsName) 325 if err != nil { 326 return false 327 } 328 return sts.Status.ReadyReplicas > 0 329 }, mediumWait, pollingInterval).Should(BeTrue(), fmt.Sprintf("Statefulset %s for component %s is not ready", stsName, componentName)) 330 }, 331 t.Entry("Checking StatefulSet opensearch-es-master", constants.VerrazzanoLoggingNamespace, opensearchoperator.ComponentName, "opensearch-es-master"), 332 t.Entry("Checking StatefulSet vmi-system-es-master", constants.VerrazzanoSystemNamespace, opensearch.ComponentName, "vmi-system-es-master"), 333 t.Entry("Checking StatefulSet keycloak", keycloak.ComponentNamespace, keycloak.ComponentName, "keycloak"), 334 t.Entry("Checking StatefulSet mysql", mysql.ComponentNamespace, mysql.ComponentName, "mysql"), 335 ) 336 }) 337 338 Context("Checking optional StatefulSets for post-upgrade", func() { 339 t.DescribeTable("StatefulSet should be ready post-upgrade", 340 func(namespace string, componentName string, stsName string) { 341 // Currently we have no way of determining if some components are installed by looking at the status (grafana) 342 // Because of this, make this test non-managed cluster only. 343 if vzcr.Spec.Profile == vzapi.ManagedCluster { 344 return 345 } 346 Eventually(func() bool { 347 if isDisabled(componentName) { 348 t.Logs.Infof("Skipping disabled component %s", componentName) 349 return true 350 } 351 sts, err := pkg.GetStatefulSet(namespace, stsName) 352 if err != nil { 353 // Deployment is optional, ignore if not found 354 // For example es-data and es-ingest won't be there for dev profile 355 if errors.IsNotFound(err) { 356 t.Logs.Infof("Skipping optional statefulSets %s since it is not found", stsName) 357 return true 358 } 359 return false 360 } 361 return sts.Status.ReadyReplicas > 0 362 }, mediumWait, pollingInterval).Should(BeTrue(), fmt.Sprintf("StatefulSet %s for component %s is not ready", stsName, componentName)) 363 }, 364 t.Entry("Checking StatefulSet opensearch-es-data", constants.VerrazzanoLoggingNamespace, opensearchoperator.ComponentName, "opensearch-es-data"), 365 t.Entry("Checking StatefulSet opensearch-es-ingest", constants.VerrazzanoLoggingNamespace, opensearchoperator.ComponentName, "opensearch-es-ingest"), 366 ) 367 }) 368 369 Context("Checking DaemonSets for post-upgrade", func() { 370 t.DescribeTable("DaemonSet should be ready post-upgrade", 371 func(namespace string, componentName string, dsName string) { 372 // Currently we have no way of determining if some components are installed by looking at the status (grafana) 373 // Because of this, make this test non-managed cluster only. 374 if vzcr.Spec.Profile == vzapi.ManagedCluster { 375 return 376 } 377 Eventually(func() bool { 378 if isDisabled(componentName) { 379 t.Logs.Infof("skipping disabled component %s", componentName) 380 return true 381 } 382 ds, err := pkg.GetDaemonSet(namespace, dsName) 383 if err != nil { 384 return false 385 } 386 return ds.Status.NumberReady > 0 387 }, mediumWait, pollingInterval).Should(BeTrue(), fmt.Sprintf("DaemonSet %s for component %s is not ready", dsName, componentName)) 388 }, 389 t.Entry("Checking StatefulSet fluentd", constants.VerrazzanoSystemNamespace, verrazzano.ComponentName, "fluentd"), 390 ) 391 }) 392 }) 393 394 var _ = t.Describe("Verify prometheus configmap reconciliation,", Label("f:platform-lcm.upgrade", "f:observability.monitoring.prom"), func() { 395 // Verify prometheus configmap is reconciled correctly 396 // GIVEN upgrade has completed 397 // WHEN the vmo pod is restarted 398 // THEN expect the vmi system prometheus config to get deleted 399 t.It("Verify prometheus configmap is deleted on vmo restart.", func() { 400 Eventually(func() bool { 401 _, _, _, err := pkg.GetPrometheusConfig() 402 return err != nil 403 }, mediumWait, pollingInterval).Should(BeTrue(), "Prometheus VMI config should be removed after upgrade.") 404 }) 405 }) 406 407 func isDisabled(componentName string) bool { 408 comp, ok := vzcr.Status.Components[componentName] 409 if ok { 410 return comp.State == vzapi.CompStateDisabled 411 } 412 return true 413 } 414 415 // getEnvoyProxyImageRef gets the envoy proxy image and its tag from bom in verrazzano-platform-operator pod running in cluster 416 func getEnvoyProxyImageRef() (string, error) { 417 bom, err := pkg.GetBOMDoc() 418 if err != nil { 419 return "", fmt.Errorf("error getting bom, %s", err.Error()) 420 } 421 422 istiodSubComponent := "istiod" 423 envoyProxyImageName := "proxyv2" 424 for _, component := range bom.Components { 425 for _, subcomponent := range component.SubComponents { 426 if subcomponent.Name == istiodSubComponent { 427 for _, image := range subcomponent.Images { 428 if strings.HasPrefix(image.ImageName, envoyProxyImageName) { 429 return fmt.Sprintf("%s:%s", image.ImageName, image.ImageTag), nil 430 } 431 } 432 } 433 } 434 435 } 436 437 return "", fmt.Errorf("envoy proxy image %s not defined in subcomponent %s in bom", envoyProxyImageName, istiodSubComponent) 438 }