diff --git a/cli/internal/helm/BUILD.bazel b/cli/internal/helm/BUILD.bazel index c568bbf18..000796570 100644 --- a/cli/internal/helm/BUILD.bazel +++ b/cli/internal/helm/BUILD.bazel @@ -9,6 +9,7 @@ go_library( "helm.go", "installer.go", "loader.go", + "symwalk.go", "values.go", ], embedsrcs = [ @@ -397,6 +398,8 @@ go_library( "charts/aws-load-balancer-controller/templates/servicemonitor.yaml", "charts/aws-load-balancer-controller/templates/webhook.yaml", "charts/aws-load-balancer-controller/values.yaml", + "charts/aws-load-balancer-controller/crds/kustomization.yaml", + "charts/aws-load-balancer-controller/test.yaml", ], importpath = "github.com/edgelesssys/constellation/v2/cli/internal/helm", visibility = ["//cli:__subpackages__"], diff --git a/cli/internal/helm/charts/aws-load-balancer-controller/crds/kustomization.yaml b/cli/internal/helm/charts/aws-load-balancer-controller/crds/kustomization.yaml new file mode 100644 index 000000000..3f1d1cbba --- /dev/null +++ b/cli/internal/helm/charts/aws-load-balancer-controller/crds/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- crds.yaml diff --git a/cli/internal/helm/charts/aws-load-balancer-controller/test.yaml b/cli/internal/helm/charts/aws-load-balancer-controller/test.yaml new file mode 100644 index 000000000..3c4a90754 --- /dev/null +++ b/cli/internal/helm/charts/aws-load-balancer-controller/test.yaml @@ -0,0 +1,326 @@ +# Default values for aws-load-balancer-controller. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 2 + +image: + repository: public.ecr.aws/eks/aws-load-balancer-controller + tag: v2.5.3 + pullPolicy: IfNotPresent + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: + # Automount API credentials for a Service Account. + automountServiceAccountToken: true + # List of image pull secrets to add to the Service Account. + imagePullSecrets: + # - name: docker + +rbac: + # Specifies whether rbac resources should be created + create: true + +podSecurityContext: + fsGroup: 65534 + +securityContext: + # capabilities: + # drop: + # - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + allowPrivilegeEscalation: false + +# Time period for the controller pod to do a graceful shutdown +terminationGracePeriodSeconds: 10 + +resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi + +# priorityClassName specifies the PriorityClass to indicate the importance of controller pods +# ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass +priorityClassName: system-cluster-critical + +nodeSelector: {} + +tolerations: [] + +# affinity specifies a custom affinity for the controller pods +affinity: {} + +# configureDefaultAffinity specifies whether to configure a default affinity for the controller pods to prevent +# co-location on the same node. This will get ignored if you specify a custom affinity configuration. +configureDefaultAffinity: true + +# topologySpreadConstraints is a stable feature of k8s v1.19 which provides the ability to +# control how Pods are spread across your cluster among failure-domains such as regions, zones, +# nodes, and other user-defined topology domains. +# +# more details here: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ +topologySpreadConstraints: {} + +updateStrategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + +# serviceAnnotations contains annotations to be added to the provisioned webhook service resource +serviceAnnotations: {} + +# deploymentAnnotations contains annotations for the controller deployment +deploymentAnnotations: {} + +podAnnotations: {} + +podLabels: {} + +# additionalLabels -- Labels to add to each object of the chart. +additionalLabels: {} + +# Enable cert-manager +enableCertManager: false + +# The name of the Kubernetes cluster. A non-empty value is required +clusterName: test-cluster + +# cluster contains configurations specific to the kubernetes cluster +cluster: + # Cluster DNS domain (required for requesting TLS certificates) + dnsDomain: cluster.local + +# The ingress class this controller will satisfy. If not specified, controller will match all +# ingresses without ingress class annotation and ingresses of type alb +ingressClass: alb + +# ingressClassParams specify the IngressCLassParams that enforce settings for a set of Ingresses when using with ingress Controller. +ingressClassParams: + create: true + # The name of ingressClassParams resource will be referred in ingressClass + name: + spec: {} + # You always can set specifications in `helm install` command through `--set` or `--set-string` + # If you do want to specify specifications in values.yaml, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'spec:'. + # namespaceSelector: + # matchLabels: + # group: + # scheme: + # ipAddressType: + # tags: + +# To use IngressClass resource instead of annotation, before you need to install the IngressClass resource pointing to controller. +# If specified as true, the IngressClass resource will be created. +createIngressClassResource: true + +# The AWS region for the kubernetes cluster. Set to use KIAM or kube2iam for example. +region: + +# The VPC ID for the Kubernetes cluster. Set this manually when your pods are unable to use the metadata service to determine this automatically +vpcId: + +# Custom AWS API Endpoints (serviceID1=URL1,serviceID2=URL2) +awsApiEndpoints: + +# awsApiThrottle specifies custom AWS API throttle settings (serviceID1:operationRegex1=rate:burst,serviceID2:operationRegex2=rate:burst) +# example: --set awsApiThrottle="{Elastic Load Balancing v2:RegisterTargets|DeregisterTargets=4:20,Elastic Load Balancing v2:.*=10:40}" +awsApiThrottle: + +# Maximum retries for AWS APIs (default 10) +awsMaxRetries: + + + + +# If enabled, targetHealth readiness gate will get injected to the pod spec for the matching endpoint pods (default true) +enablePodReadinessGateInject: + +# Enable Shield addon for ALB (default true) +enableShield: + +# Enable WAF addon for ALB (default true) +enableWaf: + +# Enable WAF V2 addon for ALB (default true) +enableWafv2: + +# Maximum number of concurrently running reconcile loops for ingress (default 3) +ingressMaxConcurrentReconciles: + +# Set the controller log level - info(default), debug (default "info") +logLevel: + +# The address the metric endpoint binds to. (default ":8080") +metricsBindAddr: "" + +# The TCP port the Webhook server binds to. (default 9443) +webhookBindPort: + +# webhookTLS specifies TLS cert/key for the webhook +webhookTLS: + caCert: + cert: + key: + +# array of namespace selectors for the webhook +webhookNamespaceSelectors: + - key: elbv2.k8s.aws/pod-readiness-gate-inject + operator: In + values: + - enabled + +# keepTLSSecret specifies whether to reuse existing TLS secret for chart upgrade +keepTLSSecret: true + +# Maximum number of concurrently running reconcile loops for service (default 3) +serviceMaxConcurrentReconciles: + +# Maximum number of concurrently running reconcile loops for targetGroupBinding +targetgroupbindingMaxConcurrentReconciles: + +# Maximum duration of exponential backoff for targetGroupBinding reconcile failures +targetgroupbindingMaxExponentialBackoffDelay: + +# Period at which the controller forces the repopulation of its local object stores. (default 1h0m0s) +syncPeriod: + +# Namespace the controller watches for updates to Kubernetes objects, If empty, all namespaces are watched. +watchNamespace: + +# disableIngressClassAnnotation disables the usage of kubernetes.io/ingress.class annotation, false by default +disableIngressClassAnnotation: + +# disableIngressGroupNameAnnotation disables the usage of alb.ingress.kubernetes.io/group.name annotation, false by default +disableIngressGroupNameAnnotation: + +# defaultSSLPolicy specifies the default SSL policy to use for TLS/HTTPS listeners +defaultSSLPolicy: + +# Liveness probe configuration for the controller +livenessProbe: + failureThreshold: 2 + httpGet: + path: /healthz + port: 61779 + scheme: HTTP + initialDelaySeconds: 30 + timeoutSeconds: 10 + +# Environment variables to set for aws-load-balancer-controller pod. +# We strongly discourage programming access credentials in the controller environment. You should setup IRSA or +# comparable solutions like kube2iam, kiam etc instead. +env: +# ENV_1: "" +# ENV_2: "" + +# Specifies if aws-load-balancer-controller should be started in hostNetwork mode. +# +# This is required if using a custom CNI where the managed control plane nodes are unable to initiate +# network connections to the pods, for example using Calico CNI plugin on EKS. This is not required or +# recommended if using the Amazon VPC CNI plugin. +hostNetwork: false + +# Specifies the dnsPolicy that should be used for pods in the deployment +# +# This may need to be used to be changed given certain conditions. For instance, if one uses the cilium CNI +# with certain settings, one may need to set `hostNetwork: true` and webhooks won't work unless `dnsPolicy` +# is set to `ClusterFirstWithHostNet`. See https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/#pod-s-dns-policy +dnsPolicy: + +# extraVolumeMounts are the additional volume mounts. This enables setting up IRSA on non-EKS Kubernetes cluster +extraVolumeMounts: + - name: aws-iam-token + mountPath: /var/run/secrets/eks.amazonaws.com/serviceaccount + readOnly: true + +# extraVolumes for the extraVolumeMounts. Useful to mount a projected service account token for example. +extraVolumes: + - name: aws-iam-token + projected: + defaultMode: 420 + sources: + - serviceAccountToken: + audience: sts.amazonaws.com + expirationSeconds: 86400 + path: token + +# defaultTags are the tags to apply to all AWS resources managed by this controller +defaultTags: + default_tag1: value1 + default_tag2: value2 + +# podDisruptionBudget specifies the disruption budget for the controller pods. +# Disruption budget will be configured only when the replicaCount is greater than 1 +podDisruptionBudget: + maxUnavailable: 1 + +# externalManagedTags is the list of tag keys on AWS resources that will be managed externally +externalManagedTags: [] + +# enableEndpointSlices enables k8s EndpointSlices for IP targets instead of Endpoints (default false) +enableEndpointSlices: + +# enableBackendSecurityGroup enables shared security group for backend traffic (default true) +enableBackendSecurityGroup: + +# backendSecurityGroup specifies backend security group id (default controller auto create backend security group) +backendSecurityGroup: + +# disableRestrictedSecurityGroupRules specifies whether to disable creating port-range restricted security group rules for traffic +disableRestrictedSecurityGroupRules: + +# controllerConfig specifies controller configuration +controllerConfig: + # featureGates set of key: value pairs that describe AWS load balance controller features + featureGates: {} + # ServiceTypeLoadBalancerOnly: true + # EndpointsFailOpen: true + +# objectSelector for webhook +objectSelector: + matchExpressions: + # - key: + # operator: + # values: + # - + matchLabels: + # key: value + +serviceMonitor: + # Specifies whether a service monitor should be created + enabled: false + # Labels to add to the service account + additionalLabels: {} + # Prometheus scrape interval + interval: 1m + # Namespace to create the service monitor in + namespace: + +# clusterSecretsPermissions lets you configure RBAC permissions for secret resources +# Access to secrets resource is required only if you use the OIDC feature, and instead of +# enabling access to all secrets, we recommend configuring namespaced role/rolebinding. +# This option is for backwards compatibility only, and will potentially be deprecated in future. +clusterSecretsPermissions: + # allowAllSecrets allows the controller to access all secrets in the cluster. + # This is to get backwards compatible behavior, but *NOT* recommended for security reasons + allowAllSecrets: false + +# ingressClassConfig contains configurations specific to the ingress class +ingressClassConfig: + default: false diff --git a/cli/internal/helm/loader.go b/cli/internal/helm/loader.go index ebe3c5540..e14ec5233 100644 --- a/cli/internal/helm/loader.go +++ b/cli/internal/helm/loader.go @@ -12,7 +12,6 @@ import ( "encoding/base64" "encoding/json" "fmt" - "io/fs" "os" "path/filepath" "strings" @@ -36,6 +35,7 @@ import ( //go:generate ./generateCilium.sh //go:generate ./update-csi-charts.sh //go:generate ./generateCertManager.sh +//go:generate ./update-aws-load-balancer-controller.sh //go:embed all:charts/* var helmFS embed.FS @@ -639,12 +639,14 @@ func loadChartsDir(efs embed.FS, dir string) (*chart.Chart, error) { rules.AddDefaults() files := []*loader.BufferedFile{} + dir += string(filepath.Separator) // add trailing dash to match rules with pattern: dir/file.yaml - walk := func(path string, d fs.DirEntry, err error) error { + walkFn := func(path string, fi os.FileInfo, err error) error { + fmt.Println("walk", path, dir) n := strings.TrimPrefix(path, dir) if n == "" { // No need to process top level. Avoid bug with helmignore .* matching - // empty names. See issue https://github.com/kubernetes/helm/issues/1776. + // empty names. See issue 1779. return nil } @@ -656,12 +658,7 @@ func loadChartsDir(efs embed.FS, dir string) (*chart.Chart, error) { return err } - fi, err := d.Info() - if err != nil { - return err - } - - if d.IsDir() { + if fi.IsDir() { // Directory-based ignore rules should involve skipping the entire // contents of that directory. if rules.Ignore(n, fi) { @@ -671,7 +668,9 @@ func loadChartsDir(efs embed.FS, dir string) (*chart.Chart, error) { } // If a .helmignore file matches, skip this file. + fmt.Println("before ignore", n, fi.Name()) if rules.Ignore(n, fi) { + fmt.Print("Ignoring file: ", n, "\n") return nil } @@ -694,7 +693,7 @@ func loadChartsDir(efs embed.FS, dir string) (*chart.Chart, error) { return nil } - if err := fs.WalkDir(efs, dir, walk); err != nil { + if err := walk(dir, walkFn); err != nil { return c, err } diff --git a/cli/internal/helm/loader_test.go b/cli/internal/helm/loader_test.go index a3035443c..317e046de 100644 --- a/cli/internal/helm/loader_test.go +++ b/cli/internal/helm/loader_test.go @@ -51,12 +51,12 @@ func TestLoad(t *testing.T) { assert.NotNil(chart.Dependencies()) } -func TestLoadDeploy(t *testing.T) { - chart, err := loadChartsDir(helmFS, constellationServicesInfo.path) // helmFS, "./charts/edgeless/constellation-services/charts/aws-load-balancer-controller") // +func TestIgnoreFilesInSubdirectory(t *testing.T) { + fileToIgnore := "crds/kustomization.yaml" + chart, err := loadChartsDir(helmFS, awsInfo.path) require.NoError(t, err) for _, f := range chart.Raw { - // fmt.Println("UNFILTERED", f.Name) - if strings.Contains(f.Name, "charts/aws-load-balancer-controller/crds/kustomization.yaml") { + if strings.Contains(f.Name, fileToIgnore) { t.Error("helmignore should have filtered it out", f.Name) } } diff --git a/cli/internal/helm/symwalk.go b/cli/internal/helm/symwalk.go new file mode 100644 index 000000000..09d1b395e --- /dev/null +++ b/cli/internal/helm/symwalk.go @@ -0,0 +1,107 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +package helm + +import ( + "log" + "os" + "path/filepath" + "sort" + + "github.com/pkg/errors" +) + +// copied from 3.12.1 "helm.sh/helm/v3/internal/sympath" + +// Walk walks the file tree rooted at root, calling walkFn for each file or directory +// in the tree, including root. All errors that arise visiting files and directories +// are filtered by walkFn. The files are walked in lexical order, which makes the +// output deterministic but means that for very large directories Walk can be +// inefficient. Walk follows symbolic links. +func walk(root string, walkFn filepath.WalkFunc) error { + info, err := os.Lstat(root) + if err != nil { + err = walkFn(root, nil, err) + } else { + err = symwalk(root, info, walkFn) + } + if err == filepath.SkipDir { + return nil + } + return err +} + +// readDirNames reads the directory named by dirname and returns +// a sorted list of directory entries. +func readDirNames(dirname string) ([]string, error) { + f, err := os.Open(dirname) + if err != nil { + return nil, err + } + names, err := f.Readdirnames(-1) + f.Close() + if err != nil { + return nil, err + } + sort.Strings(names) + return names, nil +} + +// symwalk recursively descends path, calling walkFn. +func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error { + // Recursively walk symlinked directories. + if isSymlink(info) { + resolved, err := filepath.EvalSymlinks(path) + if err != nil { + return errors.Wrapf(err, "error evaluating symlink %s", path) + } + log.Printf("found symbolic link in path: %s resolves to %s. Contents of linked file included and used", path, resolved) + if info, err = os.Lstat(resolved); err != nil { + return err + } + if err := symwalk(path, info, walkFn); err != nil && err != filepath.SkipDir { + return err + } + return nil + } + + if err := walkFn(path, info, nil); err != nil { + return err + } + + if !info.IsDir() { + return nil + } + + names, err := readDirNames(path) + if err != nil { + return walkFn(path, info, err) + } + + for _, name := range names { + filename := filepath.Join(path, name) + fileInfo, err := os.Lstat(filename) + if err != nil { + if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { + return err + } + } else { + err = symwalk(filename, fileInfo, walkFn) + if err != nil { + if (!fileInfo.IsDir() && !isSymlink(fileInfo)) || err != filepath.SkipDir { + return err + } + } + } + } + return nil +} + +// isSymlink is used to determine if the fileinfo is a symbolic link. +func isSymlink(fi os.FileInfo) bool { + return fi.Mode()&os.ModeSymlink != 0 +} diff --git a/cli/internal/helm/update-aws-load-balancer-chart.sh b/cli/internal/helm/update-aws-load-balancer-chart.sh index d2d9f1564..6bfd1baac 100755 --- a/cli/internal/helm/update-aws-load-balancer-chart.sh +++ b/cli/internal/helm/update-aws-load-balancer-chart.sh @@ -4,7 +4,7 @@ # script is mostly copied from cli/internal/helm/update-csi-charts.sh set -euo pipefail -set -o errtrac +set -o errtrace shopt -s inherit_errexit echo "Updating AWS Load Balancer Controller Helm chart..." @@ -35,12 +35,13 @@ git clone \ git sparse-checkout add "${chart_dir}" git checkout cd "${callDir}" -rm "${repo_tmp_dir}/${chart_dir}/crds/kustomization.yaml" -rm "${repo_tmp_dir}/${chart_dir}/test.yaml" + +# remove values.yaml from upstream chart rm "${repo_tmp_dir}/${chart_dir}/values.yaml" -# remove old chart -rm -r "${chart_base_path:?}/${chart_name}" +# delete current chart +# but keep values.yaml +find "${chart_base_path:?}/${chart_name}" -mindepth 1 -maxdepth 1 ! -name "values.yaml" -exec rm -r {} + # move new chart mkdir -p "${chart_base_path}/${chart_name}"