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

     1  // Copyright (c) 2022, 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  	"fmt"
     8  	"github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1"
     9  	"github.com/verrazzano/verrazzano/tools/vz/pkg/helpers"
    10  	"io"
    11  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    12  	"k8s.io/apimachinery/pkg/runtime/schema"
    13  	"os"
    14  	clipkg "sigs.k8s.io/controller-runtime/pkg/client"
    15  	"strings"
    16  
    17  	"k8s.io/apimachinery/pkg/util/strategicpatch"
    18  	"sigs.k8s.io/yaml"
    19  )
    20  
    21  // MergeYAMLFiles parses the given slice of filenames containing yaml and
    22  // merges them into a single verrazzano yaml and then returned as a vz resource.
    23  func MergeYAMLFiles(filenames []string, stdinReader io.Reader) (*unstructured.Unstructured, error) {
    24  	var vzYAML string
    25  	var stdin bool
    26  	var gv = &schema.GroupVersion{}
    27  	for _, filename := range filenames {
    28  		var readBytes []byte
    29  		var err error
    30  		// filename of "-" is for reading from stdin
    31  		if filename == "-" {
    32  			if stdin {
    33  				continue
    34  			}
    35  			stdin = true
    36  			readBytes, err = io.ReadAll(stdinReader)
    37  		} else {
    38  			readBytes, err = os.ReadFile(strings.TrimSpace(filename))
    39  		}
    40  		if err != nil {
    41  			return nil, err
    42  		}
    43  		if err := checkGroupVersion(readBytes, gv); err != nil {
    44  			return nil, err
    45  		}
    46  		vzYAML, err = overlayVerrazzano(*gv, vzYAML, string(readBytes))
    47  		if err != nil {
    48  			return nil, err
    49  		}
    50  	}
    51  
    52  	vz := &unstructured.Unstructured{}
    53  	err := yaml.Unmarshal([]byte(vzYAML), &vz)
    54  	if err != nil {
    55  		return nil, fmt.Errorf("Failed to create a verrazzano install resource: %s", err.Error())
    56  	}
    57  	if vz.GetNamespace() == "" {
    58  		vz.SetNamespace("default")
    59  	}
    60  	if vz.GetName() == "" {
    61  		vz.SetName("verrazzano")
    62  	}
    63  
    64  	return vz, nil
    65  }
    66  
    67  // MergeSetFlags merges yaml representing a set flag passed on the command line with a
    68  // verrazano install resource.  A merged verrazzano install resource is returned.
    69  func MergeSetFlags(gv schema.GroupVersion, vz clipkg.Object, overlayYAML string) (clipkg.Object, *unstructured.Unstructured, error) {
    70  	baseYAML, err := yaml.Marshal(vz)
    71  	if err != nil {
    72  		return vz, nil, err
    73  	}
    74  	vzYAML, err := overlayVerrazzano(gv, string(baseYAML), overlayYAML)
    75  	if err != nil {
    76  		return vz, nil, err
    77  	}
    78  
    79  	obj := &unstructured.Unstructured{}
    80  	err = yaml.Unmarshal([]byte(vzYAML), obj)
    81  	if err != nil {
    82  		return obj, nil, fmt.Errorf("Failed to create a verrazzano install resource: %s", err.Error())
    83  	}
    84  	return obj, obj, nil
    85  }
    86  
    87  // MergeSetFlagsUpgrade takes the existing Verrazzano resource and merges yaml representing a set flag passed on the command line with a
    88  // A merged verrazzano install resource is returned.
    89  func MergeSetFlagsUpgrade(gv schema.GroupVersion, vz *v1beta1.Verrazzano, overlayYAML string) (clipkg.Object, *unstructured.Unstructured, error) {
    90  	vz.APIVersion = "install.verrazzano.io/v1beta1"
    91  	vz.Kind = "Verrazzano"
    92  	baseYAML, err := yaml.Marshal(vz)
    93  	if err != nil {
    94  		return vz, nil, err
    95  	}
    96  
    97  	vzYAML, err := overlayVerrazzano(gv, string(baseYAML), overlayYAML)
    98  	if err != nil {
    99  		return vz, nil, err
   100  	}
   101  
   102  	obj := &unstructured.Unstructured{}
   103  	err = yaml.Unmarshal([]byte(vzYAML), obj)
   104  	if err != nil {
   105  		return obj, nil, fmt.Errorf("Failed to create a verrazzano install resource: %s", err.Error())
   106  	}
   107  	return obj, obj, nil
   108  }
   109  
   110  // overlayVerrazzano overlays over base using JSON strategic merge.
   111  func overlayVerrazzano(gv schema.GroupVersion, baseYAML string, overlayYAML string) (string, error) {
   112  	if strings.TrimSpace(baseYAML) == "" {
   113  		return overlayYAML, nil
   114  	}
   115  	if strings.TrimSpace(overlayYAML) == "" {
   116  		return baseYAML, nil
   117  	}
   118  	baseJSON, err := yaml.YAMLToJSON([]byte(baseYAML))
   119  	if err != nil {
   120  		return "", fmt.Errorf("Failed to create a verrazzano install resource: %s\n%s", err.Error(), baseYAML)
   121  	}
   122  	overlayJSON, err := yaml.YAMLToJSON([]byte(overlayYAML))
   123  	if err != nil {
   124  		return "", fmt.Errorf("Failed to create a verrazzano install resource: %s\n%s", err.Error(), overlayYAML)
   125  	}
   126  
   127  	// Merge the two json representations
   128  	mergedJSON, err := strategicpatch.StrategicMergePatch(baseJSON, overlayJSON, helpers.NewVerrazzanoForGroupVersion(gv)())
   129  	if err != nil {
   130  		return "", fmt.Errorf("Failed to merge yaml: %s\n base object:\n%s\n override object:\n%s", err.Error(), baseJSON, overlayJSON)
   131  	}
   132  
   133  	mergedYAML, err := yaml.JSONToYAML(mergedJSON)
   134  	if err != nil {
   135  		return "", fmt.Errorf("Failed to create a verrazzano install resource: %s\n%s", err.Error(), mergedJSON)
   136  	}
   137  
   138  	return string(mergedYAML), nil
   139  }
   140  
   141  func checkGroupVersion(readBytes []byte, gv *schema.GroupVersion) error {
   142  	obj := &unstructured.Unstructured{}
   143  	if err := yaml.Unmarshal(readBytes, obj); err != nil {
   144  		// allow merge of unknown objects
   145  		return nil
   146  	}
   147  	ogv := obj.GroupVersionKind().GroupVersion()
   148  	if gv.Version == "" {
   149  		*gv = ogv
   150  	} else {
   151  		if ogv != *gv {
   152  			return fmt.Errorf("cannot merge objects with different group versions: %v != %v", *gv, ogv)
   153  		}
   154  	}
   155  	return nil
   156  }