...

Source file src/github.com/GoogleCloudPlatform/k8s-config-connector/operator/scripts/generate-image-configmap/main.go

Documentation: github.com/GoogleCloudPlatform/k8s-config-connector/operator/scripts/generate-image-configmap

     1  // Copyright 2022 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // This script will extract images of KCC components from stable `manifest.yaml` and generate a ConfigMap `image_configmap.yaml` under config/release directory.
    16  // The ConfigMap will be used for Component Release Pipeline to pre-load and validate images deployed by the operator.
    17  //
    18  // run `make manifests' to invoke the script, or directly go run scripts/generate-image-configmap/main.go"
    19  package main
    20  
    21  import (
    22  	"context"
    23  	"encoding/json"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"log"
    27  	"path"
    28  	"strings"
    29  
    30  	"github.com/ghodss/yaml"
    31  	appsv1 "k8s.io/api/apps/v1"
    32  	corev1 "k8s.io/api/core/v1"
    33  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    34  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    35  	"sigs.k8s.io/kubebuilder-declarative-pattern/pkg/patterns/declarative/pkg/manifest"
    36  
    37  	corev1beta1 "github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/apis/core/v1beta1"
    38  	"github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/k8s"
    39  	cnrmmanifest "github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/manifest"
    40  	"github.com/GoogleCloudPlatform/k8s-config-connector/operator/pkg/test/util/paths"
    41  )
    42  
    43  const (
    44  	fileMode       = 0600
    45  	outputFilename = "image_configmap.yaml"
    46  	outputDir      = "config/gke-addon"
    47  )
    48  
    49  func main() {
    50  	ctx := context.Background()
    51  	cc := &corev1beta1.ConfigConnector{
    52  		Spec: corev1beta1.ConfigConnectorSpec{
    53  			Mode:                 k8s.NamespacedMode,
    54  			GoogleServiceAccount: "someGSA",
    55  		},
    56  	}
    57  	operatorSrcRoot := paths.GetOperatorSrcRootOrLogFatal()
    58  	r := cnrmmanifest.NewLocalRepository(path.Join(operatorSrcRoot, "channels"))
    59  	channel, err := r.LoadChannel(ctx, k8s.StableChannel)
    60  	if err != nil {
    61  		log.Fatalf("error loading %v channel: %v", k8s.StableChannel, err)
    62  	}
    63  	version, err := channel.Latest(ctx, cc.ComponentName())
    64  	if err != nil {
    65  		log.Fatalf("error resolving the version to deploy: %v", err)
    66  	}
    67  	if version == nil {
    68  		log.Fatalf("could not find the latest version in channel %v", k8s.StableChannel)
    69  	}
    70  
    71  	manifestStrs, err := r.LoadManifest(ctx, cc.ComponentName(), version.Version, cc)
    72  	if err != nil {
    73  		log.Fatalf("error loading manifest for package %v of version %v: %v", version.Package, version.Version, err)
    74  	}
    75  	objects := make([]*manifest.Object, 0)
    76  	for _, str := range manifestStrs {
    77  		m, err := manifest.ParseObjects(ctx, str)
    78  		if err != nil {
    79  			log.Fatalf("parsing manifest: %v", err)
    80  		}
    81  		objects = append(objects, m.Items...)
    82  	}
    83  
    84  	namespacedStrs, err := r.LoadNamespacedComponents(ctx, cc.ComponentName(), version.Version)
    85  	if err != nil {
    86  		log.Fatalf("error loading namespaced components for package %v of version %v: %v", version.Package, version.Version, err)
    87  	}
    88  	for _, str := range namespacedStrs {
    89  		m, err := manifest.ParseObjects(ctx, str)
    90  		if err != nil {
    91  			log.Fatalf("parsing manifest: %v", err)
    92  		}
    93  		objects = append(objects, m.Items...)
    94  	}
    95  
    96  	cm := corev1.ConfigMap{
    97  		TypeMeta: metav1.TypeMeta{
    98  			Kind:       "ConfigMap",
    99  			APIVersion: "v1",
   100  		},
   101  		ObjectMeta: metav1.ObjectMeta{
   102  			Name:      "config-images",
   103  			Namespace: k8s.OperatorSystemNamespace,
   104  			Labels: map[string]string{
   105  				"addonmanager.kubernetes.io/mode":       "Reconcile",
   106  				"cnrm.cloud.google.com/operator-system": "true",
   107  			},
   108  			Annotations: map[string]string{
   109  				"components.gke.io/image-map": "Images deployed by operator",
   110  			},
   111  		},
   112  		Data: make(map[string]string, 0),
   113  	}
   114  
   115  	for _, obj := range objects {
   116  		// controller image
   117  		if obj.Kind == "StatefulSet" && strings.Contains(obj.GetName(), "cnrm-controller-manager") {
   118  			image, err := extractImageFromStatefulSet(obj.UnstructuredObject(), "manager")
   119  			if err != nil {
   120  				log.Fatalf("error resolving manager image: %v", err)
   121  			}
   122  			cm.Data["cnrm.controller"] = image
   123  		}
   124  		// deletion defender image
   125  		if obj.Kind == "StatefulSet" && obj.GetName() == "cnrm-deletiondefender" {
   126  			image, err := extractImageFromStatefulSet(obj.UnstructuredObject(), "deletiondefender")
   127  			if err != nil {
   128  				log.Fatalf("error resolving manager image: %v", err)
   129  			}
   130  			cm.Data["cnrm.deletiondefender"] = image
   131  		}
   132  		// unmanaged detector image
   133  		if obj.Kind == "StatefulSet" && obj.GetName() == "cnrm-unmanaged-detector" {
   134  			image, err := extractImageFromStatefulSet(obj.UnstructuredObject(), "unmanageddetector")
   135  			if err != nil {
   136  				log.Fatalf("error resolving manager image: %v", err)
   137  			}
   138  			cm.Data["cnrm.unmanageddetector"] = image
   139  		}
   140  		// webhook image
   141  		if obj.Kind == "Deployment" && obj.GetName() == "cnrm-webhook-manager" {
   142  			image, err := extractImageFromDeployment(obj.UnstructuredObject(), "webhook")
   143  			if err != nil {
   144  				log.Fatalf("error resolving webhook image: %v", err)
   145  			}
   146  			cm.Data["cnrm.webhook"] = image
   147  		}
   148  		// recorder image
   149  		if obj.Kind == "Deployment" && obj.GetName() == "cnrm-resource-stats-recorder" {
   150  			image, err := extractImageFromDeployment(obj.UnstructuredObject(), "recorder")
   151  			if err != nil {
   152  				log.Fatalf("error resolving recorder image: %v", err)
   153  			}
   154  			cm.Data["cnrm.recorder"] = image
   155  		}
   156  		// prom-to-sd sidecar image
   157  		if obj.Kind == "Deployment" && obj.GetName() == "cnrm-resource-stats-recorder" {
   158  			image, err := extractImageFromDeployment(obj.UnstructuredObject(), "prom-to-sd")
   159  			if err != nil {
   160  				log.Fatalf("error resolving prom-to-sd sidecar image: %v", err)
   161  			}
   162  			cm.Data["prom-to-sd"] = image
   163  		}
   164  	}
   165  	outputFilepath := path.Join(operatorSrcRoot, outputDir, outputFilename)
   166  	if err := outputConfigMapToFile(&cm, outputFilepath); err != nil {
   167  		log.Fatalf("error writing ConfigMap %v to file: %v", cm, err)
   168  	}
   169  	log.Println("successfully generated the image_configmaps.yaml")
   170  }
   171  
   172  func extractImageFromStatefulSet(obj *unstructured.Unstructured, containerName string) (string, error) {
   173  	b, err := obj.MarshalJSON()
   174  	if err != nil {
   175  		return "", err
   176  	}
   177  	ss := &appsv1.StatefulSet{}
   178  	if err := json.Unmarshal(b, ss); err != nil {
   179  		return "", err
   180  	}
   181  
   182  	for _, container := range ss.Spec.Template.Spec.Containers {
   183  		if container.Name == containerName {
   184  			return container.Image, nil
   185  		}
   186  	}
   187  	return "", fmt.Errorf("could not find container with name %v in StatefulSet %v", containerName, obj.GetName())
   188  }
   189  
   190  func extractImageFromDeployment(obj *unstructured.Unstructured, containerName string) (string, error) {
   191  	b, err := obj.MarshalJSON()
   192  	if err != nil {
   193  		return "", err
   194  	}
   195  	ss := &appsv1.Deployment{}
   196  	if err := json.Unmarshal(b, ss); err != nil {
   197  		return "", err
   198  	}
   199  
   200  	for _, container := range ss.Spec.Template.Spec.Containers {
   201  		if container.Name == containerName {
   202  			return container.Image, nil
   203  		}
   204  	}
   205  	return "", fmt.Errorf("could not find container with name %v in Deployment %v", containerName, obj.GetName())
   206  }
   207  
   208  func outputConfigMapToFile(crd *corev1.ConfigMap, outputFilepath string) error {
   209  	crdBytes, err := yaml.Marshal(crd)
   210  	if err != nil {
   211  		return err
   212  	}
   213  	if err := ioutil.WriteFile(outputFilepath, crdBytes, fileMode); err != nil {
   214  		return err
   215  	}
   216  	return nil
   217  }
   218  

View as plain text