/* Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ package gcp import ( "context" "encoding/json" "fmt" "strings" "github.com/edgelesssys/constellation/v2/internal/gcpshared" "github.com/edgelesssys/constellation/v2/internal/kubernetes" "github.com/edgelesssys/constellation/v2/internal/versions" k8s "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // CloudControllerManager holds the gcp cloud-controller-manager configuration. type CloudControllerManager struct { uid string projectID string } // NewCloudControllerManager returns an initialized cloud controller manager configuration struct for GCP. func NewCloudControllerManager(metadata *Metadata) (*CloudControllerManager, error) { uid, err := metadata.api.UID() if err != nil { return nil, fmt.Errorf("getting uid from metadata: %w", err) } projectID, err := metadata.api.RetrieveProjectID() if err != nil { return nil, fmt.Errorf("getting project id from metadata: %w", err) } return &CloudControllerManager{ uid: uid, projectID: projectID, }, nil } // Image returns the container image used to provide cloud-controller-manager for the cloud-provider. func (c *CloudControllerManager) Image(k8sVersion versions.ValidK8sVersion) (string, error) { return versions.VersionConfigs[k8sVersion].CloudControllerManagerImageGCP, nil } // Path returns the path used by cloud-controller-manager executable within the container image. func (c *CloudControllerManager) Path() string { return "/cloud-controller-manager" } // Name returns the cloud-provider name as used by k8s cloud-controller-manager (k8s.gcr.io/cloud-controller-manager). func (c *CloudControllerManager) Name() string { return "gce" } // ExtraArgs returns a list of arguments to append to the cloud-controller-manager command. func (c *CloudControllerManager) ExtraArgs() []string { return []string{ "--use-service-account-credentials", "--controllers=cloud-node,cloud-node-lifecycle,nodeipam,service,route", "--cloud-config=/etc/gce/gce.conf", "--cidr-allocator-type=CloudAllocator", "--allocate-node-cidrs=true", "--configure-cloud-routes=false", } } // ConfigMaps returns a list of ConfigMaps to deploy together with the k8s cloud-controller-manager // Reference: https://kubernetes.io/docs/concepts/configuration/configmap/ . func (c *CloudControllerManager) ConfigMaps() (kubernetes.ConfigMaps, error) { // GCP CCM expects cloud config to contain the GCP project-id and other configuration. // reference: https://github.com/kubernetes/cloud-provider-gcp/blob/master/cluster/gce/gci/configure-helper.sh#L791-L892 var config strings.Builder config.WriteString("[global]\n") config.WriteString(fmt.Sprintf("project-id = %s\n", c.projectID)) config.WriteString("use-metadata-server = true\n") config.WriteString(fmt.Sprintf("node-tags = constellation-%s\n", c.uid)) return kubernetes.ConfigMaps{ &k8s.ConfigMap{ TypeMeta: v1.TypeMeta{ Kind: "ConfigMap", APIVersion: "v1", }, ObjectMeta: v1.ObjectMeta{ Name: "gceconf", Namespace: "kube-system", }, Data: map[string]string{ "gce.conf": config.String(), }, }, }, nil } // Secrets returns a list of secrets to deploy together with the k8s cloud-controller-manager. // Reference: https://kubernetes.io/docs/concepts/configuration/secret/ . func (c *CloudControllerManager) Secrets(_ context.Context, _ string, cloudServiceAccountURI string) (kubernetes.Secrets, error) { serviceAccountKey, err := gcpshared.ServiceAccountKeyFromURI(cloudServiceAccountURI) if err != nil { return kubernetes.Secrets{}, err } rawKey, err := json.Marshal(serviceAccountKey) if err != nil { return kubernetes.Secrets{}, err } return kubernetes.Secrets{ &k8s.Secret{ TypeMeta: v1.TypeMeta{ Kind: "Secret", APIVersion: "v1", }, ObjectMeta: v1.ObjectMeta{ Name: "gcekey", Namespace: "kube-system", }, Data: map[string][]byte{ "key.json": rawKey, }, }, }, nil } // Volumes returns a list of volumes to deploy together with the k8s cloud-controller-manager. // Reference: https://kubernetes.io/docs/concepts/storage/volumes/ . func (c *CloudControllerManager) Volumes() []k8s.Volume { return []k8s.Volume{ { Name: "gceconf", VolumeSource: k8s.VolumeSource{ ConfigMap: &k8s.ConfigMapVolumeSource{ LocalObjectReference: k8s.LocalObjectReference{ Name: "gceconf", }, }, }, }, { Name: "gcekey", VolumeSource: k8s.VolumeSource{ Secret: &k8s.SecretVolumeSource{ SecretName: "gcekey", }, }, }, } } // VolumeMounts returns a list of volume mounts to deploy together with the k8s cloud-controller-manager. func (c *CloudControllerManager) VolumeMounts() []k8s.VolumeMount { return []k8s.VolumeMount{ { Name: "gceconf", ReadOnly: true, MountPath: "/etc/gce", }, { Name: "gcekey", ReadOnly: true, MountPath: "/var/secrets/google", }, } } // Env returns a list of k8s environment key-value pairs to deploy together with the k8s cloud-controller-manager. func (c *CloudControllerManager) Env() []k8s.EnvVar { return []k8s.EnvVar{ { Name: "GOOGLE_APPLICATION_CREDENTIALS", Value: "/var/secrets/google/key.json", }, } } // Supported is used to determine if cloud controller manager is implemented for this cloud provider. func (c *CloudControllerManager) Supported() bool { return true }