github.com/verrazzano/verrazzano@v1.7.0/tools/vz/cmd/helpers/operator_yaml.go (about)

     1  // Copyright (c) 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 helpers
     5  
     6  import (
     7  	"bufio"
     8  	"fmt"
     9  	"io/fs"
    10  	"os"
    11  	"strings"
    12  
    13  	"github.com/verrazzano/verrazzano/pkg/k8sutil"
    14  	vzos "github.com/verrazzano/verrazzano/pkg/os"
    15  	vpoconst "github.com/verrazzano/verrazzano/platform-operator/constants"
    16  	"github.com/verrazzano/verrazzano/tools/vz/pkg/constants"
    17  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    18  )
    19  
    20  // updateOperatorYAMLPrivateRegistry edits the operator yaml file after copying it to a new temp file
    21  func updateOperatorYAMLPrivateRegistry(operatorFilename string, imageRegistry string, imagePrefix string) (string, error) {
    22  	var err error
    23  
    24  	fileObj, err := os.Open(operatorFilename)
    25  	defer func() { fileObj.Close() }()
    26  	if err != nil {
    27  		return operatorFilename, err
    28  	}
    29  	objectsInYAML, err := k8sutil.Unmarshall(bufio.NewReader(fileObj))
    30  	if err != nil {
    31  		return "", err
    32  	}
    33  	vpoDeployIdx, vpoWebhookDeployIdx := findVPODeploymentIndices(objectsInYAML)
    34  	vpoDeploy := &objectsInYAML[vpoDeployIdx]
    35  	vpoWebhookDeploy := &objectsInYAML[vpoWebhookDeployIdx]
    36  
    37  	vpoDeployUpdated, err := updatePrivateRegistryVPODeploy(vpoDeploy, imageRegistry, imagePrefix, true)
    38  	if err != nil {
    39  		return "", err
    40  	}
    41  	vpoWebhookDeployUpdated, err := updatePrivateRegistryVPODeploy(vpoWebhookDeploy, imageRegistry, imagePrefix, false)
    42  	if err != nil {
    43  		return "", err
    44  	}
    45  	if !vpoDeployUpdated && !vpoWebhookDeployUpdated {
    46  		return operatorFilename, nil
    47  	}
    48  	objectsInYAML[vpoDeployIdx] = *vpoDeploy
    49  	objectsInYAML[vpoWebhookDeployIdx] = *vpoWebhookDeploy
    50  	bytesToWrite, err := k8sutil.Marshal(objectsInYAML)
    51  	if err != nil {
    52  		return "", err
    53  	}
    54  
    55  	var tempFile *os.File
    56  	if tempFile, err = vzos.CreateTempFile("vz-operator-file", nil); err != nil {
    57  		return "", err
    58  	}
    59  	editedOperatorFile := tempFile.Name()
    60  	if err := os.WriteFile(editedOperatorFile, bytesToWrite, fs.ModeAppend); err != nil {
    61  		return "", err
    62  	}
    63  	return editedOperatorFile, nil
    64  }
    65  
    66  func findVPODeploymentIndices(objectsInYAML []unstructured.Unstructured) (int, int) {
    67  	vpoDeployIdx := -1
    68  	vpoWebhookDeployIdx := -1
    69  	for idx, yamlObj := range objectsInYAML {
    70  		if yamlObj.GetObjectKind().GroupVersionKind().Kind == "Deployment" {
    71  			if yamlObj.GetName() == constants.VerrazzanoPlatformOperator {
    72  				vpoDeployIdx = idx
    73  			} else if yamlObj.GetName() == constants.VerrazzanoPlatformOperatorWebhook {
    74  				vpoWebhookDeployIdx = idx
    75  			}
    76  		}
    77  	}
    78  	return vpoDeployIdx, vpoWebhookDeployIdx
    79  }
    80  
    81  // updatePrivateRegistryVPODeploy updates the private registry information in the
    82  // given verrazzano-platform-operator (or webhook) deployment YAML. Returns true if vpoDeploy was modified
    83  func updatePrivateRegistryVPODeploy(vpoDeploy *unstructured.Unstructured, imageRegistry string, imagePrefix string, addRegistryEnvVars bool) (bool, error) {
    84  	vpoDeployObj := vpoDeploy.Object
    85  	containersFields := containersFields()
    86  	initContainersFields := initContainersFields()
    87  
    88  	containers, found, err := unstructured.NestedSlice(vpoDeployObj, containersFields...)
    89  	if err != nil || !found {
    90  		return false, fmt.Errorf("Failed to find containers in verrazzano-platform-operator deployment")
    91  	}
    92  
    93  	initContainers, found, err := unstructured.NestedSlice(vpoDeployObj, initContainersFields...)
    94  	if err != nil || !found {
    95  		return false, fmt.Errorf("Failed to find initContainers in verrazzano-platform-operator deployment")
    96  	}
    97  
    98  	updated := false
    99  	for idx := range initContainers {
   100  		// Use indexing on the slice to get a reference to the initCtr so we can edit it. By default
   101  		// range returns copies so our edits won't stick
   102  		initCtr := initContainers[idx].(map[string]interface{})
   103  		ctrUpdated := updatePrivateRegistryOnContainer(initCtr, imageRegistry, imagePrefix)
   104  		updated = updated || ctrUpdated
   105  	}
   106  
   107  	for idx := range containers {
   108  		// Use indexing on the slice to get a reference to the container so we can edit it. By default
   109  		// range returns copies so our edits won't stick
   110  		container := containers[idx].(map[string]interface{})
   111  		ctrUpdated := updatePrivateRegistryOnContainer(container, imageRegistry, imagePrefix)
   112  		updated = updated || ctrUpdated
   113  		if container["name"] == constants.VerrazzanoPlatformOperator {
   114  			envUpdated := addRegistryEnvVarsToContainer(container, imageRegistry, imagePrefix)
   115  			updated = updated || envUpdated
   116  		}
   117  	}
   118  
   119  	if updated {
   120  		if err := unstructured.SetNestedSlice(vpoDeployObj, containers, containersFields...); err != nil {
   121  			return false, err
   122  		}
   123  		if err := unstructured.SetNestedSlice(vpoDeployObj, initContainers, initContainersFields...); err != nil {
   124  			return false, err
   125  		}
   126  		vpoDeploy.SetUnstructuredContent(vpoDeployObj)
   127  		return true, nil
   128  	}
   129  	return false, nil
   130  }
   131  
   132  func addRegistryEnvVarsToContainer(container map[string]interface{}, imageRegistry string, imagePrefix string) bool {
   133  	foundRegistry := false
   134  	foundPrefix := false
   135  	updated := false
   136  	env := container["env"].([]interface{})
   137  	if env == nil {
   138  		env = make([]interface{}, 2)
   139  	}
   140  	for idx := range env {
   141  		envVar := env[idx].(map[string]interface{})
   142  		if envVar["name"] == vpoconst.RegistryOverrideEnvVar {
   143  			foundRegistry = true
   144  			if envVar["value"] != imageRegistry {
   145  				envVar["value"] = imageRegistry
   146  				updated = true
   147  			}
   148  		}
   149  		if envVar["name"] == vpoconst.ImageRepoOverrideEnvVar {
   150  			foundPrefix = true
   151  			if envVar["value"] != imagePrefix {
   152  				envVar["value"] = imagePrefix
   153  				updated = true
   154  			}
   155  		}
   156  	}
   157  	if !foundRegistry {
   158  		env = append(env, map[string]interface{}{
   159  			"name":  vpoconst.RegistryOverrideEnvVar,
   160  			"value": imageRegistry,
   161  		})
   162  		updated = true
   163  	}
   164  	if !foundPrefix {
   165  		env = append(env, map[string]interface{}{
   166  			"name":  vpoconst.ImageRepoOverrideEnvVar,
   167  			"value": imagePrefix,
   168  		})
   169  		updated = true
   170  	}
   171  	container["env"] = env
   172  	return updated
   173  }
   174  
   175  func updatePrivateRegistryOnContainer(container map[string]interface{}, imageRegistry string, imagePrefix string) bool {
   176  	curImage := container["image"].(string)
   177  	suffixPattern := fmt.Sprintf("/verrazzano/%s", constants.VerrazzanoPlatformOperator)
   178  	imageSuffix := curImage[strings.LastIndex(curImage, suffixPattern)+1:]
   179  	newImage := fmt.Sprintf("%s/%s/%s", imageRegistry, imagePrefix, imageSuffix)
   180  	if newImage == curImage {
   181  		// no update needed
   182  		return false
   183  	}
   184  	container["image"] = newImage
   185  	return true
   186  }
   187  
   188  func containersFields() []string {
   189  	return []string{"spec", "template", "spec", "containers"}
   190  }
   191  
   192  func initContainersFields() []string {
   193  	return []string{"spec", "template", "spec", "initContainers"}
   194  }