From 2b08e4ac959fed92c20a441fe01e2628d20c098c Mon Sep 17 00:00:00 2001 From: Austin Abro Date: Fri, 28 Jun 2024 12:47:57 +0000 Subject: [PATCH] feat: flux OCI support in Zarf Agent --- examples/podinfo-flux/flux-install.yaml | 4195 ----------------- .../{ => git}/podinfo-kustomization.yaml | 4 +- .../{ => git}/podinfo-source.yaml | 2 +- .../helm/podinfo-helmrelease.yaml | 17 + .../podinfo-flux/helm/podinfo-source.yaml | 9 + .../oci/podinfo-kustomization.yaml | 14 + examples/podinfo-flux/oci/podinfo-source.yaml | 11 + examples/podinfo-flux/zarf.yaml | 90 +- go.mod | 25 +- go.sum | 65 +- .../zarf-agent/manifests/clusterrole.yaml | 12 + .../manifests/clusterrolebinding.yaml | 12 + packages/zarf-agent/manifests/webhook.yaml | 86 + packages/zarf-agent/zarf.yaml | 2 + site/src/content/docs/ref/actions.mdx | 2 +- site/src/content/docs/ref/components.mdx | 2 +- site/src/content/docs/ref/init-package.mdx | 16 +- src/config/lang/english.go | 7 +- .../agent/hooks/{flux.go => flux-gitrepo.go} | 0 .../{flux_test.go => flux-gitrepo_test.go} | 0 src/internal/agent/hooks/flux-helmrepo.go | 106 + .../agent/hooks/flux-helmrepo_test.go | 179 + src/internal/agent/hooks/flux-ocirepo.go | 133 + src/internal/agent/hooks/flux-ocirepo_test.go | 208 + src/internal/agent/hooks/utils_test.go | 1 + src/internal/agent/http/server.go | 16 +- src/internal/packager/helm/post-render.go | 7 +- src/pkg/cluster/secrets.go | 48 +- src/pkg/cluster/secrets_test.go | 64 +- src/pkg/cluster/tunnel.go | 18 +- src/pkg/cluster/tunnel_test.go | 10 +- src/pkg/transform/image.go | 4 +- src/pkg/transform/image_test.go | 4 + src/test/e2e/22_git_and_gitops_test.go | 42 +- src/test/external/common.go | 15 +- src/test/external/ext_in_cluster_test.go | 80 +- src/test/external/ext_out_cluster_test.go | 40 +- src/test/external/registries.yaml | 4 + 38 files changed, 1211 insertions(+), 4339 deletions(-) delete mode 100644 examples/podinfo-flux/flux-install.yaml rename examples/podinfo-flux/{ => git}/podinfo-kustomization.yaml (81%) rename examples/podinfo-flux/{ => git}/podinfo-source.yaml (95%) create mode 100644 examples/podinfo-flux/helm/podinfo-helmrelease.yaml create mode 100644 examples/podinfo-flux/helm/podinfo-source.yaml create mode 100644 examples/podinfo-flux/oci/podinfo-kustomization.yaml create mode 100644 examples/podinfo-flux/oci/podinfo-source.yaml create mode 100644 packages/zarf-agent/manifests/clusterrole.yaml create mode 100644 packages/zarf-agent/manifests/clusterrolebinding.yaml rename src/internal/agent/hooks/{flux.go => flux-gitrepo.go} (100%) rename src/internal/agent/hooks/{flux_test.go => flux-gitrepo_test.go} (100%) create mode 100644 src/internal/agent/hooks/flux-helmrepo.go create mode 100644 src/internal/agent/hooks/flux-helmrepo_test.go create mode 100644 src/internal/agent/hooks/flux-ocirepo.go create mode 100644 src/internal/agent/hooks/flux-ocirepo_test.go create mode 100644 src/test/external/registries.yaml diff --git a/examples/podinfo-flux/flux-install.yaml b/examples/podinfo-flux/flux-install.yaml deleted file mode 100644 index 50128a3b4e..0000000000 --- a/examples/podinfo-flux/flux-install.yaml +++ /dev/null @@ -1,4195 +0,0 @@ ---- -# This manifest was generated by flux. DO NOT EDIT. -# Flux Version: v0.33.0 -# Components: source-controller,kustomize-controller -apiVersion: v1 -kind: Namespace -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - pod-security.kubernetes.io/warn: restricted - pod-security.kubernetes.io/warn-version: latest - name: flux-system ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - name: buckets.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: Bucket - listKind: BucketList - plural: buckets - singular: bucket - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.endpoint - name: Endpoint - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: Bucket is the Schema for the buckets API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: BucketSpec defines the desired state of an S3 compatible - bucket - properties: - accessFrom: - description: AccessFrom defines an Access Control List for allowing - cross-namespace references to this object. - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - bucketName: - description: The bucket name. - type: string - endpoint: - description: The bucket endpoint address. - type: string - ignore: - description: Ignore overrides the set of excluded patterns in the - .sourceignore format (which is the same as .gitignore). If not provided, - a default will be used, consult the documentation for your version - to find out what those are. - type: string - insecure: - description: Insecure allows connecting to a non-TLS S3 HTTP endpoint. - type: boolean - interval: - description: The interval at which to check for bucket updates. - type: string - provider: - default: generic - description: The S3 compatible storage provider name, default ('generic'). - enum: - - generic - - aws - - gcp - type: string - region: - description: The bucket region. - type: string - secretRef: - description: The name of the secret containing authentication credentials - for the Bucket. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - timeout: - default: 60s - description: The timeout for download operations, defaults to 60s. - type: string - required: - - bucketName - - endpoint - - interval - type: object - status: - default: - observedGeneration: -1 - description: BucketStatus defines the observed state of a bucket - properties: - artifact: - description: Artifact represents the output of the last successful - Bucket sync. - properties: - checksum: - description: Checksum is the SHA256 checksum of the artifact. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of this artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: Revision is a human readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm index timestamp, a Helm chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the Bucket. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the artifact output of the - last Bucket sync. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.endpoint - name: Endpoint - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1beta2 - schema: - openAPIV3Schema: - description: Bucket is the Schema for the buckets API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: BucketSpec specifies the required configuration to produce - an Artifact for an object storage bucket. - properties: - accessFrom: - description: 'AccessFrom specifies an Access Control List for allowing - cross-namespace references to this object. NOTE: Not implemented, - provisional as of https://github.com/fluxcd/flux2/pull/2092' - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - bucketName: - description: BucketName is the name of the object storage bucket. - type: string - endpoint: - description: Endpoint is the object storage address the BucketName - is located at. - type: string - ignore: - description: Ignore overrides the set of excluded patterns in the - .sourceignore format (which is the same as .gitignore). If not provided, - a default will be used, consult the documentation for your version - to find out what those are. - type: string - insecure: - description: Insecure allows connecting to a non-TLS HTTP Endpoint. - type: boolean - interval: - description: Interval at which to check the Endpoint for updates. - type: string - provider: - default: generic - description: Provider of the object storage bucket. Defaults to 'generic', - which expects an S3 (API) compatible object storage. - enum: - - generic - - aws - - gcp - - azure - type: string - region: - description: Region of the Endpoint where the BucketName is located - in. - type: string - secretRef: - description: SecretRef specifies the Secret containing authentication - credentials for the Bucket. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: Suspend tells the controller to suspend the reconciliation - of this Bucket. - type: boolean - timeout: - default: 60s - description: Timeout for fetch operations, defaults to 60s. - type: string - required: - - bucketName - - endpoint - - interval - type: object - status: - default: - observedGeneration: -1 - description: BucketStatus records the observed state of a Bucket. - properties: - artifact: - description: Artifact represents the last successful Bucket reconciliation. - properties: - checksum: - description: Checksum is the SHA256 checksum of the Artifact file. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of the Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: Path is the relative file path of the Artifact. It - can be used to locate the file in the root of the Artifact storage - on the local file system of the controller managing the Source. - type: string - revision: - description: Revision is a human-readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: URL is the HTTP address of the Artifact as exposed - by the controller managing the Source. It can be used to retrieve - the Artifact for consumption, e.g. by another controller applying - the Artifact contents. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the Bucket. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation of - the Bucket object. - format: int64 - type: integer - url: - description: URL is the dynamic fetch link for the latest Artifact. - It is provided on a "best effort" basis, and using the precise BucketStatus.Artifact - data is recommended. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - name: gitrepositories.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: GitRepository - listKind: GitRepositoryList - plural: gitrepositories - shortNames: - - gitrepo - singular: gitrepository - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: GitRepository is the Schema for the gitrepositories API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: GitRepositorySpec defines the desired state of a Git repository. - properties: - accessFrom: - description: AccessFrom defines an Access Control List for allowing - cross-namespace references to this object. - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - gitImplementation: - default: go-git - description: Determines which git client library to use. Defaults - to go-git, valid values are ('go-git', 'libgit2'). - enum: - - go-git - - libgit2 - type: string - ignore: - description: Ignore overrides the set of excluded patterns in the - .sourceignore format (which is the same as .gitignore). If not provided, - a default will be used, consult the documentation for your version - to find out what those are. - type: string - include: - description: Extra git repositories to map into the repository - items: - description: GitRepositoryInclude defines a source with a from and - to path. - properties: - fromPath: - description: The path to copy contents from, defaults to the - root directory. - type: string - repository: - description: Reference to a GitRepository to include. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - toPath: - description: The path to copy contents to, defaults to the name - of the source ref. - type: string - required: - - repository - type: object - type: array - interval: - description: The interval at which to check for repository updates. - type: string - recurseSubmodules: - description: When enabled, after the clone is created, initializes - all submodules within, using their default settings. This option - is available only when using the 'go-git' GitImplementation. - type: boolean - ref: - description: The Git reference to checkout and monitor for changes, - defaults to master branch. - properties: - branch: - description: The Git branch to checkout, defaults to master. - type: string - commit: - description: The Git commit SHA to checkout, if specified Tag - filters will be ignored. - type: string - semver: - description: The Git tag semver expression, takes precedence over - Tag. - type: string - tag: - description: The Git tag to checkout, takes precedence over Branch. - type: string - type: object - secretRef: - description: The secret name containing the Git credentials. For HTTPS - repositories the secret must contain username and password fields. - For SSH repositories the secret must contain identity and known_hosts - fields. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - timeout: - default: 60s - description: The timeout for remote Git operations like cloning, defaults - to 60s. - type: string - url: - description: The repository URL, can be a HTTP/S or SSH address. - pattern: ^(http|https|ssh)://.*$ - type: string - verify: - description: Verify OpenPGP signature for the Git commit HEAD points - to. - properties: - mode: - description: Mode describes what git object should be verified, - currently ('head'). - enum: - - head - type: string - secretRef: - description: The secret name containing the public keys of all - trusted Git authors. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - mode - type: object - required: - - interval - - url - type: object - status: - default: - observedGeneration: -1 - description: GitRepositoryStatus defines the observed state of a Git repository. - properties: - artifact: - description: Artifact represents the output of the last successful - repository sync. - properties: - checksum: - description: Checksum is the SHA256 checksum of the artifact. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of this artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: Revision is a human readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm index timestamp, a Helm chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the GitRepository. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - includedArtifacts: - description: IncludedArtifacts represents the included artifacts from - the last successful repository sync. - items: - description: Artifact represents the output of a source synchronisation. - properties: - checksum: - description: Checksum is the SHA256 checksum of the artifact. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of this artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: Revision is a human readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm index timestamp, a Helm chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - path - - url - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the artifact output of the - last repository sync. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1beta2 - schema: - openAPIV3Schema: - description: GitRepository is the Schema for the gitrepositories API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: GitRepositorySpec specifies the required configuration to - produce an Artifact for a Git repository. - properties: - accessFrom: - description: 'AccessFrom specifies an Access Control List for allowing - cross-namespace references to this object. NOTE: Not implemented, - provisional as of https://github.com/fluxcd/flux2/pull/2092' - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - gitImplementation: - default: go-git - description: GitImplementation specifies which Git client library - implementation to use. Defaults to 'go-git', valid values are ('go-git', - 'libgit2'). - enum: - - go-git - - libgit2 - type: string - ignore: - description: Ignore overrides the set of excluded patterns in the - .sourceignore format (which is the same as .gitignore). If not provided, - a default will be used, consult the documentation for your version - to find out what those are. - type: string - include: - description: Include specifies a list of GitRepository resources which - Artifacts should be included in the Artifact produced for this GitRepository. - items: - description: GitRepositoryInclude specifies a local reference to - a GitRepository which Artifact (sub-)contents must be included, - and where they should be placed. - properties: - fromPath: - description: FromPath specifies the path to copy contents from, - defaults to the root of the Artifact. - type: string - repository: - description: GitRepositoryRef specifies the GitRepository which - Artifact contents must be included. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - toPath: - description: ToPath specifies the path to copy contents to, - defaults to the name of the GitRepositoryRef. - type: string - required: - - repository - type: object - type: array - interval: - description: Interval at which to check the GitRepository for updates. - type: string - recurseSubmodules: - description: RecurseSubmodules enables the initialization of all submodules - within the GitRepository as cloned from the URL, using their default - settings. This option is available only when using the 'go-git' - GitImplementation. - type: boolean - ref: - description: Reference specifies the Git reference to resolve and - monitor for changes, defaults to the 'master' branch. - properties: - branch: - description: "Branch to check out, defaults to 'master' if no - other field is defined. \n When GitRepositorySpec.GitImplementation - is set to 'go-git', a shallow clone of the specified branch - is performed." - type: string - commit: - description: "Commit SHA to check out, takes precedence over all - reference fields. \n When GitRepositorySpec.GitImplementation - is set to 'go-git', this can be combined with Branch to shallow - clone the branch, in which the commit is expected to exist." - type: string - semver: - description: SemVer tag expression to check out, takes precedence - over Tag. - type: string - tag: - description: Tag to check out, takes precedence over Branch. - type: string - type: object - secretRef: - description: SecretRef specifies the Secret containing authentication - credentials for the GitRepository. For HTTPS repositories the Secret - must contain 'username' and 'password' fields. For SSH repositories - the Secret must contain 'identity' and 'known_hosts' fields. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: Suspend tells the controller to suspend the reconciliation - of this GitRepository. - type: boolean - timeout: - default: 60s - description: Timeout for Git operations like cloning, defaults to - 60s. - type: string - url: - description: URL specifies the Git repository URL, it can be an HTTP/S - or SSH address. - pattern: ^(http|https|ssh)://.*$ - type: string - verify: - description: Verification specifies the configuration to verify the - Git commit signature(s). - properties: - mode: - description: Mode specifies what Git object should be verified, - currently ('head'). - enum: - - head - type: string - secretRef: - description: SecretRef specifies the Secret containing the public - keys of trusted Git authors. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - mode - type: object - required: - - interval - - url - type: object - status: - default: - observedGeneration: -1 - description: GitRepositoryStatus records the observed state of a Git repository. - properties: - artifact: - description: Artifact represents the last successful GitRepository - reconciliation. - properties: - checksum: - description: Checksum is the SHA256 checksum of the Artifact file. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of the Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: Path is the relative file path of the Artifact. It - can be used to locate the file in the root of the Artifact storage - on the local file system of the controller managing the Source. - type: string - revision: - description: Revision is a human-readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: URL is the HTTP address of the Artifact as exposed - by the controller managing the Source. It can be used to retrieve - the Artifact for consumption, e.g. by another controller applying - the Artifact contents. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the GitRepository. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - contentConfigChecksum: - description: 'ContentConfigChecksum is a checksum of all the configurations - related to the content of the source artifact: - .spec.ignore - - .spec.recurseSubmodules - .spec.included and the checksum of the - included artifacts observed in .status.observedGeneration version - of the object. This can be used to determine if the content of the - included repository has changed. It has the format of `:`, - for example: `sha256:`.' - type: string - includedArtifacts: - description: IncludedArtifacts contains a list of the last successfully - included Artifacts as instructed by GitRepositorySpec.Include. - items: - description: Artifact represents the output of a Source reconciliation. - properties: - checksum: - description: Checksum is the SHA256 checksum of the Artifact - file. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of the Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI - annotations. - type: object - path: - description: Path is the relative file path of the Artifact. - It can be used to locate the file in the root of the Artifact - storage on the local file system of the controller managing - the Source. - type: string - revision: - description: Revision is a human-readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: URL is the HTTP address of the Artifact as exposed - by the controller managing the Source. It can be used to retrieve - the Artifact for consumption, e.g. by another controller applying - the Artifact contents. - type: string - required: - - path - - url - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation of - the GitRepository object. - format: int64 - type: integer - url: - description: URL is the dynamic fetch link for the latest Artifact. - It is provided on a "best effort" basis, and using the precise GitRepositoryStatus.Artifact - data is recommended. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - name: helmcharts.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: HelmChart - listKind: HelmChartList - plural: helmcharts - shortNames: - - hc - singular: helmchart - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.chart - name: Chart - type: string - - jsonPath: .spec.version - name: Version - type: string - - jsonPath: .spec.sourceRef.kind - name: Source Kind - type: string - - jsonPath: .spec.sourceRef.name - name: Source Name - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: HelmChart is the Schema for the helmcharts API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: HelmChartSpec defines the desired state of a Helm chart. - properties: - accessFrom: - description: AccessFrom defines an Access Control List for allowing - cross-namespace references to this object. - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - chart: - description: The name or path the Helm chart is available at in the - SourceRef. - type: string - interval: - description: The interval at which to check the Source for updates. - type: string - reconcileStrategy: - default: ChartVersion - description: Determines what enables the creation of a new artifact. - Valid values are ('ChartVersion', 'Revision'). See the documentation - of the values for an explanation on their behavior. Defaults to - ChartVersion when omitted. - enum: - - ChartVersion - - Revision - type: string - sourceRef: - description: The reference to the Source the chart is available at. - properties: - apiVersion: - description: APIVersion of the referent. - type: string - kind: - description: Kind of the referent, valid values are ('HelmRepository', - 'GitRepository', 'Bucket'). - enum: - - HelmRepository - - GitRepository - - Bucket - type: string - name: - description: Name of the referent. - type: string - required: - - kind - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - valuesFile: - description: Alternative values file to use as the default chart values, - expected to be a relative path in the SourceRef. Deprecated in favor - of ValuesFiles, for backwards compatibility the file defined here - is merged before the ValuesFiles items. Ignored when omitted. - type: string - valuesFiles: - description: Alternative list of values files to use as the chart - values (values.yaml is not included by default), expected to be - a relative path in the SourceRef. Values files are merged in the - order of this list with the last file overriding the first. Ignored - when omitted. - items: - type: string - type: array - version: - default: '*' - description: The chart version semver expression, ignored for charts - from GitRepository and Bucket sources. Defaults to latest when omitted. - type: string - required: - - chart - - interval - - sourceRef - type: object - status: - default: - observedGeneration: -1 - description: HelmChartStatus defines the observed state of the HelmChart. - properties: - artifact: - description: Artifact represents the output of the last successful - chart sync. - properties: - checksum: - description: Checksum is the SHA256 checksum of the artifact. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of this artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: Revision is a human readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm index timestamp, a Helm chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmChart. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the last chart pulled. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.chart - name: Chart - type: string - - jsonPath: .spec.version - name: Version - type: string - - jsonPath: .spec.sourceRef.kind - name: Source Kind - type: string - - jsonPath: .spec.sourceRef.name - name: Source Name - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1beta2 - schema: - openAPIV3Schema: - description: HelmChart is the Schema for the helmcharts API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: HelmChartSpec specifies the desired state of a Helm chart. - properties: - accessFrom: - description: 'AccessFrom specifies an Access Control List for allowing - cross-namespace references to this object. NOTE: Not implemented, - provisional as of https://github.com/fluxcd/flux2/pull/2092' - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - chart: - description: Chart is the name or path the Helm chart is available - at in the SourceRef. - type: string - interval: - description: Interval is the interval at which to check the Source - for updates. - type: string - reconcileStrategy: - default: ChartVersion - description: ReconcileStrategy determines what enables the creation - of a new artifact. Valid values are ('ChartVersion', 'Revision'). - See the documentation of the values for an explanation on their - behavior. Defaults to ChartVersion when omitted. - enum: - - ChartVersion - - Revision - type: string - sourceRef: - description: SourceRef is the reference to the Source the chart is - available at. - properties: - apiVersion: - description: APIVersion of the referent. - type: string - kind: - description: Kind of the referent, valid values are ('HelmRepository', - 'GitRepository', 'Bucket'). - enum: - - HelmRepository - - GitRepository - - Bucket - type: string - name: - description: Name of the referent. - type: string - required: - - kind - - name - type: object - suspend: - description: Suspend tells the controller to suspend the reconciliation - of this source. - type: boolean - valuesFile: - description: ValuesFile is an alternative values file to use as the - default chart values, expected to be a relative path in the SourceRef. - Deprecated in favor of ValuesFiles, for backwards compatibility - the file specified here is merged before the ValuesFiles items. - Ignored when omitted. - type: string - valuesFiles: - description: ValuesFiles is an alternative list of values files to - use as the chart values (values.yaml is not included by default), - expected to be a relative path in the SourceRef. Values files are - merged in the order of this list with the last file overriding the - first. Ignored when omitted. - items: - type: string - type: array - version: - default: '*' - description: Version is the chart version semver expression, ignored - for charts from GitRepository and Bucket sources. Defaults to latest - when omitted. - type: string - required: - - chart - - interval - - sourceRef - type: object - status: - default: - observedGeneration: -1 - description: HelmChartStatus records the observed state of the HelmChart. - properties: - artifact: - description: Artifact represents the output of the last successful - reconciliation. - properties: - checksum: - description: Checksum is the SHA256 checksum of the Artifact file. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of the Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: Path is the relative file path of the Artifact. It - can be used to locate the file in the root of the Artifact storage - on the local file system of the controller managing the Source. - type: string - revision: - description: Revision is a human-readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: URL is the HTTP address of the Artifact as exposed - by the controller managing the Source. It can be used to retrieve - the Artifact for consumption, e.g. by another controller applying - the Artifact contents. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmChart. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedChartName: - description: ObservedChartName is the last observed chart name as - specified by the resolved chart reference. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation of - the HelmChart object. - format: int64 - type: integer - observedSourceArtifactRevision: - description: ObservedSourceArtifactRevision is the last observed Artifact.Revision - of the HelmChartSpec.SourceRef. - type: string - url: - description: URL is the dynamic fetch link for the latest Artifact. - It is provided on a "best effort" basis, and using the precise BucketStatus.Artifact - data is recommended. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - name: helmrepositories.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: HelmRepository - listKind: HelmRepositoryList - plural: helmrepositories - shortNames: - - helmrepo - singular: helmrepository - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: HelmRepository is the Schema for the helmrepositories API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: HelmRepositorySpec defines the reference to a Helm repository. - properties: - accessFrom: - description: AccessFrom defines an Access Control List for allowing - cross-namespace references to this object. - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - interval: - description: The interval at which to check the upstream for updates. - type: string - passCredentials: - description: PassCredentials allows the credentials from the SecretRef - to be passed on to a host that does not match the host as defined - in URL. This may be required if the host of the advertised chart - URLs in the index differ from the defined URL. Enabling this should - be done with caution, as it can potentially result in credentials - getting stolen in a MITM-attack. - type: boolean - secretRef: - description: The name of the secret containing authentication credentials - for the Helm repository. For HTTP/S basic auth the secret must contain - username and password fields. For TLS the secret must contain a - certFile and keyFile, and/or caCert fields. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - timeout: - default: 60s - description: The timeout of index downloading, defaults to 60s. - type: string - url: - description: The Helm repository URL, a valid URL contains at least - a protocol and host. - type: string - required: - - interval - - url - type: object - status: - default: - observedGeneration: -1 - description: HelmRepositoryStatus defines the observed state of the HelmRepository. - properties: - artifact: - description: Artifact represents the output of the last successful - repository sync. - properties: - checksum: - description: Checksum is the SHA256 checksum of the artifact. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of this artifact. - format: date-time - type: string - path: - description: Path is the relative file path of this artifact. - type: string - revision: - description: Revision is a human readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm index timestamp, a Helm chart version, etc. - type: string - url: - description: URL is the HTTP address of this artifact. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmRepository. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the last index fetched. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1beta2 - schema: - openAPIV3Schema: - description: HelmRepository is the Schema for the helmrepositories API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: HelmRepositorySpec specifies the required configuration to - produce an Artifact for a Helm repository index YAML. - properties: - accessFrom: - description: 'AccessFrom specifies an Access Control List for allowing - cross-namespace references to this object. NOTE: Not implemented, - provisional as of https://github.com/fluxcd/flux2/pull/2092' - properties: - namespaceSelectors: - description: NamespaceSelectors is the list of namespace selectors - to which this ACL applies. Items in this list are evaluated - using a logical OR operation. - items: - description: NamespaceSelector selects the namespaces to which - this ACL applies. An empty map of MatchLabels matches all - namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: MatchLabels is a map of {key,value} pairs. - A single {key,value} in the matchLabels map is equivalent - to an element of matchExpressions, whose key field is - "key", the operator is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - interval: - description: Interval at which to check the URL for updates. - type: string - passCredentials: - description: PassCredentials allows the credentials from the SecretRef - to be passed on to a host that does not match the host as defined - in URL. This may be required if the host of the advertised chart - URLs in the index differ from the defined URL. Enabling this should - be done with caution, as it can potentially result in credentials - getting stolen in a MITM-attack. - type: boolean - provider: - default: generic - description: Provider used for authentication, can be 'aws', 'azure', - 'gcp' or 'generic'. This field is optional, and only taken into - account if the .spec.type field is set to 'oci'. When not specified, - defaults to 'generic'. - enum: - - generic - - aws - - azure - - gcp - type: string - secretRef: - description: SecretRef specifies the Secret containing authentication - credentials for the HelmRepository. For HTTP/S basic auth the secret - must contain 'username' and 'password' fields. For TLS the secret - must contain a 'certFile' and 'keyFile', and/or 'caCert' fields. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: Suspend tells the controller to suspend the reconciliation - of this HelmRepository. - type: boolean - timeout: - default: 60s - description: Timeout is used for the index fetch operation for an - HTTPS helm repository, and for remote OCI Repository operations - like pulling for an OCI helm repository. Its default value is 60s. - type: string - type: - description: Type of the HelmRepository. When this field is set to "oci", - the URL field value must be prefixed with "oci://". - enum: - - default - - oci - type: string - url: - description: URL of the Helm repository, a valid URL contains at least - a protocol and host. - type: string - required: - - interval - - url - type: object - status: - default: - observedGeneration: -1 - description: HelmRepositoryStatus records the observed state of the HelmRepository. - properties: - artifact: - description: Artifact represents the last successful HelmRepository - reconciliation. - properties: - checksum: - description: Checksum is the SHA256 checksum of the Artifact file. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of the Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: Path is the relative file path of the Artifact. It - can be used to locate the file in the root of the Artifact storage - on the local file system of the controller managing the Source. - type: string - revision: - description: Revision is a human-readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: URL is the HTTP address of the Artifact as exposed - by the controller managing the Source. It can be used to retrieve - the Artifact for consumption, e.g. by another controller applying - the Artifact contents. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmRepository. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation of - the HelmRepository object. - format: int64 - type: integer - url: - description: URL is the dynamic fetch link for the latest Artifact. - It is provided on a "best effort" basis, and using the precise HelmRepositoryStatus.Artifact - data is recommended. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - name: kustomizations.kustomize.toolkit.fluxcd.io -spec: - group: kustomize.toolkit.fluxcd.io - names: - kind: Kustomization - listKind: KustomizationList - plural: kustomizations - shortNames: - - ks - singular: kustomization - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta1 - schema: - openAPIV3Schema: - description: Kustomization is the Schema for the kustomizations API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: KustomizationSpec defines the desired state of a kustomization. - properties: - decryption: - description: Decrypt Kubernetes secrets before applying them on the - cluster. - properties: - provider: - description: Provider is the name of the decryption engine. - enum: - - sops - type: string - secretRef: - description: The secret name containing the private OpenPGP keys - used for decryption. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - provider - type: object - dependsOn: - description: DependsOn may contain a meta.NamespacedObjectReference - slice with references to Kustomization resources that must be ready - before this Kustomization can be reconciled. - items: - description: NamespacedObjectReference contains enough information - to locate the referenced Kubernetes resource object in any namespace. - properties: - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it - acts as LocalObjectReference. - type: string - required: - - name - type: object - type: array - force: - default: false - description: Force instructs the controller to recreate resources - when patching fails due to an immutable field change. - type: boolean - healthChecks: - description: A list of resources to be included in the health assessment. - items: - description: NamespacedObjectKindReference contains enough information - to locate the typed referenced Kubernetes resource object in any - namespace. - properties: - apiVersion: - description: API version of the referent, if not specified the - Kubernetes preferred version will be used. - type: string - kind: - description: Kind of the referent. - type: string - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it - acts as LocalObjectReference. - type: string - required: - - kind - - name - type: object - type: array - images: - description: Images is a list of (image name, new name, new tag or - digest) for changing image names, tags or digests. This can also - be achieved with a patch, but this operator is simpler to specify. - items: - description: Image contains an image name, a new name, a new tag - or digest, which will replace the original name and tag. - properties: - digest: - description: Digest is the value used to replace the original - image tag. If digest is present NewTag value is ignored. - type: string - name: - description: Name is a tag-less image name. - type: string - newName: - description: NewName is the value used to replace the original - name. - type: string - newTag: - description: NewTag is the value used to replace the original - tag. - type: string - required: - - name - type: object - type: array - interval: - description: The interval at which to reconcile the Kustomization. - type: string - kubeConfig: - description: The KubeConfig for reconciling the Kustomization on a - remote cluster. When specified, KubeConfig takes precedence over - ServiceAccountName. - properties: - secretRef: - description: SecretRef holds the name to a secret that contains - a 'value' key with the kubeconfig file as the value. It must - be in the same namespace as the Kustomization. It is recommended - that the kubeconfig is self-contained, and the secret is regularly - updated if credentials such as a cloud-access-token expire. - Cloud specific `cmd-path` auth helpers will not function without - adding binaries and credentials to the Pod that is responsible - for reconciling the Kustomization. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - type: object - patches: - description: Strategic merge and JSON patches, defined as inline YAML - objects, capable of targeting objects based on kind, label and annotation - selectors. - items: - description: Patch contains an inline StrategicMerge or JSON6902 - patch, and the target the patch should be applied to. - properties: - patch: - description: Patch contains an inline StrategicMerge patch or - an inline JSON6902 patch with an array of operation objects. - type: string - target: - description: Target points to the resources that the patch document - should be applied to. - properties: - annotationSelector: - description: AnnotationSelector is a string that follows - the label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: Group is the API group to select resources - from. Together with Version and Kind it is capable of - unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: LabelSelector is a string that follows the - label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: Version of the API Group to select resources - from. Together with Group and Kind it is capable of unambiguously - identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - type: object - type: array - patchesJson6902: - description: JSON 6902 patches, defined as inline YAML objects. - items: - description: JSON6902Patch contains a JSON6902 patch and the target - the patch should be applied to. - properties: - patch: - description: Patch contains the JSON6902 patch document with - an array of operation objects. - items: - description: JSON6902 is a JSON6902 operation object. https://datatracker.ietf.org/doc/html/rfc6902#section-4 - properties: - from: - description: From contains a JSON-pointer value that references - a location within the target document where the operation - is performed. The meaning of the value depends on the - value of Op, and is NOT taken into account by all operations. - type: string - op: - description: Op indicates the operation to perform. Its - value MUST be one of "add", "remove", "replace", "move", - "copy", or "test". https://datatracker.ietf.org/doc/html/rfc6902#section-4 - enum: - - test - - remove - - add - - replace - - move - - copy - type: string - path: - description: Path contains the JSON-pointer value that - references a location within the target document where - the operation is performed. The meaning of the value - depends on the value of Op. - type: string - value: - description: Value contains a valid JSON structure. The - meaning of the value depends on the value of Op, and - is NOT taken into account by all operations. - x-kubernetes-preserve-unknown-fields: true - required: - - op - - path - type: object - type: array - target: - description: Target points to the resources that the patch document - should be applied to. - properties: - annotationSelector: - description: AnnotationSelector is a string that follows - the label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: Group is the API group to select resources - from. Together with Version and Kind it is capable of - unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: LabelSelector is a string that follows the - label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: Version of the API Group to select resources - from. Together with Group and Kind it is capable of unambiguously - identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - patch - - target - type: object - type: array - patchesStrategicMerge: - description: Strategic merge patches, defined as inline YAML objects. - items: - x-kubernetes-preserve-unknown-fields: true - type: array - path: - description: Path to the directory containing the kustomization.yaml - file, or the set of plain YAMLs a kustomization.yaml should be generated - for. Defaults to 'None', which translates to the root path of the - SourceRef. - type: string - postBuild: - description: PostBuild describes which actions to perform on the YAML - manifest generated by building the kustomize overlay. - properties: - substitute: - additionalProperties: - type: string - description: Substitute holds a map of key/value pairs. The variables - defined in your YAML manifests that match any of the keys defined - in the map will be substituted with the set value. Includes - support for bash string replacement functions e.g. ${var:=default}, - ${var:position} and ${var/substring/replacement}. - type: object - substituteFrom: - description: SubstituteFrom holds references to ConfigMaps and - Secrets containing the variables and their values to be substituted - in the YAML manifests. The ConfigMap and the Secret data keys - represent the var names and they must match the vars declared - in the manifests for the substitution to happen. - items: - description: SubstituteReference contains a reference to a resource - containing the variables name and value. - properties: - kind: - description: Kind of the values referent, valid values are - ('Secret', 'ConfigMap'). - enum: - - Secret - - ConfigMap - type: string - name: - description: Name of the values referent. Should reside - in the same namespace as the referring resource. - maxLength: 253 - minLength: 1 - type: string - required: - - kind - - name - type: object - type: array - type: object - prune: - description: Prune enables garbage collection. - type: boolean - retryInterval: - description: The interval at which to retry a previously failed reconciliation. - When not specified, the controller uses the KustomizationSpec.Interval - value to retry failures. - type: string - serviceAccountName: - description: The name of the Kubernetes service account to impersonate - when reconciling this Kustomization. - type: string - sourceRef: - description: Reference of the source where the kustomization file - is. - properties: - apiVersion: - description: API version of the referent - type: string - kind: - description: Kind of the referent - enum: - - GitRepository - - Bucket - type: string - name: - description: Name of the referent - type: string - namespace: - description: Namespace of the referent, defaults to the Kustomization - namespace - type: string - required: - - kind - - name - type: object - suspend: - description: This flag tells the controller to suspend subsequent - kustomize executions, it does not apply to already started executions. - Defaults to false. - type: boolean - targetNamespace: - description: TargetNamespace sets or overrides the namespace in the - kustomization.yaml file. - maxLength: 63 - minLength: 1 - type: string - timeout: - description: Timeout for validation, apply and health checking operations. - Defaults to 'Interval' duration. - type: string - validation: - description: Validate the Kubernetes objects before applying them - on the cluster. The validation strategy can be 'client' (local dry-run), - 'server' (APIServer dry-run) or 'none'. When 'Force' is 'true', - validation will fallback to 'client' if set to 'server' because - server-side validation is not supported in this scenario. - enum: - - none - - client - - server - type: string - required: - - interval - - prune - - sourceRef - type: object - status: - default: - observedGeneration: -1 - description: KustomizationStatus defines the observed state of a kustomization. - properties: - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastAppliedRevision: - description: The last successfully applied revision. The revision - format for Git sources is /. - type: string - lastAttemptedRevision: - description: LastAttemptedRevision is the revision of the last reconciliation - attempt. - type: string - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last reconciled generation. - format: int64 - type: integer - snapshot: - description: The last successfully applied revision metadata. - properties: - checksum: - description: The manifests sha1 checksum. - type: string - entries: - description: A list of Kubernetes kinds grouped by namespace. - items: - description: Snapshot holds the metadata of namespaced Kubernetes - objects - properties: - kinds: - additionalProperties: - type: string - description: The list of Kubernetes kinds. - type: object - namespace: - description: The namespace of this entry. - type: string - required: - - kinds - type: object - type: array - required: - - checksum - - entries - type: object - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1beta2 - schema: - openAPIV3Schema: - description: Kustomization is the Schema for the kustomizations API. - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: KustomizationSpec defines the configuration to calculate - the desired state from a Source using Kustomize. - properties: - decryption: - description: Decrypt Kubernetes secrets before applying them on the - cluster. - properties: - provider: - description: Provider is the name of the decryption engine. - enum: - - sops - type: string - secretRef: - description: The secret name containing the private OpenPGP keys - used for decryption. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - provider - type: object - dependsOn: - description: DependsOn may contain a meta.NamespacedObjectReference - slice with references to Kustomization resources that must be ready - before this Kustomization can be reconciled. - items: - description: NamespacedObjectReference contains enough information - to locate the referenced Kubernetes resource object in any namespace. - properties: - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it - acts as LocalObjectReference. - type: string - required: - - name - type: object - type: array - force: - default: false - description: Force instructs the controller to recreate resources - when patching fails due to an immutable field change. - type: boolean - healthChecks: - description: A list of resources to be included in the health assessment. - items: - description: NamespacedObjectKindReference contains enough information - to locate the typed referenced Kubernetes resource object in any - namespace. - properties: - apiVersion: - description: API version of the referent, if not specified the - Kubernetes preferred version will be used. - type: string - kind: - description: Kind of the referent. - type: string - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it - acts as LocalObjectReference. - type: string - required: - - kind - - name - type: object - type: array - images: - description: Images is a list of (image name, new name, new tag or - digest) for changing image names, tags or digests. This can also - be achieved with a patch, but this operator is simpler to specify. - items: - description: Image contains an image name, a new name, a new tag - or digest, which will replace the original name and tag. - properties: - digest: - description: Digest is the value used to replace the original - image tag. If digest is present NewTag value is ignored. - type: string - name: - description: Name is a tag-less image name. - type: string - newName: - description: NewName is the value used to replace the original - name. - type: string - newTag: - description: NewTag is the value used to replace the original - tag. - type: string - required: - - name - type: object - type: array - interval: - description: The interval at which to reconcile the Kustomization. - type: string - kubeConfig: - description: The KubeConfig for reconciling the Kustomization on a - remote cluster. When used in combination with KustomizationSpec.ServiceAccountName, - forces the controller to act on behalf of that Service Account at - the target cluster. If the --default-service-account flag is set, - its value will be used as a controller level fallback for when KustomizationSpec.ServiceAccountName - is empty. - properties: - secretRef: - description: SecretRef holds the name of a secret that contains - a key with the kubeconfig file as the value. If no key is set, - the key will default to 'value'. The secret must be in the same - namespace as the Kustomization. It is recommended that the kubeconfig - is self-contained, and the secret is regularly updated if credentials - such as a cloud-access-token expire. Cloud specific `cmd-path` - auth helpers will not function without adding binaries and credentials - to the Pod that is responsible for reconciling the Kustomization. - properties: - key: - description: Key in the Secret, when not specified an implementation-specific - default key is used. - type: string - name: - description: Name of the Secret. - type: string - required: - - name - type: object - type: object - patches: - description: Strategic merge and JSON patches, defined as inline YAML - objects, capable of targeting objects based on kind, label and annotation - selectors. - items: - description: Patch contains an inline StrategicMerge or JSON6902 - patch, and the target the patch should be applied to. - properties: - patch: - description: Patch contains an inline StrategicMerge patch or - an inline JSON6902 patch with an array of operation objects. - type: string - target: - description: Target points to the resources that the patch document - should be applied to. - properties: - annotationSelector: - description: AnnotationSelector is a string that follows - the label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: Group is the API group to select resources - from. Together with Version and Kind it is capable of - unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: LabelSelector is a string that follows the - label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: Version of the API Group to select resources - from. Together with Group and Kind it is capable of unambiguously - identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - type: object - type: array - patchesJson6902: - description: 'JSON 6902 patches, defined as inline YAML objects. Deprecated: - Use Patches instead.' - items: - description: JSON6902Patch contains a JSON6902 patch and the target - the patch should be applied to. - properties: - patch: - description: Patch contains the JSON6902 patch document with - an array of operation objects. - items: - description: JSON6902 is a JSON6902 operation object. https://datatracker.ietf.org/doc/html/rfc6902#section-4 - properties: - from: - description: From contains a JSON-pointer value that references - a location within the target document where the operation - is performed. The meaning of the value depends on the - value of Op, and is NOT taken into account by all operations. - type: string - op: - description: Op indicates the operation to perform. Its - value MUST be one of "add", "remove", "replace", "move", - "copy", or "test". https://datatracker.ietf.org/doc/html/rfc6902#section-4 - enum: - - test - - remove - - add - - replace - - move - - copy - type: string - path: - description: Path contains the JSON-pointer value that - references a location within the target document where - the operation is performed. The meaning of the value - depends on the value of Op. - type: string - value: - description: Value contains a valid JSON structure. The - meaning of the value depends on the value of Op, and - is NOT taken into account by all operations. - x-kubernetes-preserve-unknown-fields: true - required: - - op - - path - type: object - type: array - target: - description: Target points to the resources that the patch document - should be applied to. - properties: - annotationSelector: - description: AnnotationSelector is a string that follows - the label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: Group is the API group to select resources - from. Together with Version and Kind it is capable of - unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: LabelSelector is a string that follows the - label selection expression https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: Version of the API Group to select resources - from. Together with Group and Kind it is capable of unambiguously - identifying and/or selecting resources. https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - patch - - target - type: object - type: array - patchesStrategicMerge: - description: 'Strategic merge patches, defined as inline YAML objects. - Deprecated: Use Patches instead.' - items: - x-kubernetes-preserve-unknown-fields: true - type: array - path: - description: Path to the directory containing the kustomization.yaml - file, or the set of plain YAMLs a kustomization.yaml should be generated - for. Defaults to 'None', which translates to the root path of the - SourceRef. - type: string - postBuild: - description: PostBuild describes which actions to perform on the YAML - manifest generated by building the kustomize overlay. - properties: - substitute: - additionalProperties: - type: string - description: Substitute holds a map of key/value pairs. The variables - defined in your YAML manifests that match any of the keys defined - in the map will be substituted with the set value. Includes - support for bash string replacement functions e.g. ${var:=default}, - ${var:position} and ${var/substring/replacement}. - type: object - substituteFrom: - description: SubstituteFrom holds references to ConfigMaps and - Secrets containing the variables and their values to be substituted - in the YAML manifests. The ConfigMap and the Secret data keys - represent the var names and they must match the vars declared - in the manifests for the substitution to happen. - items: - description: SubstituteReference contains a reference to a resource - containing the variables name and value. - properties: - kind: - description: Kind of the values referent, valid values are - ('Secret', 'ConfigMap'). - enum: - - Secret - - ConfigMap - type: string - name: - description: Name of the values referent. Should reside - in the same namespace as the referring resource. - maxLength: 253 - minLength: 1 - type: string - optional: - default: false - description: Optional indicates whether the referenced resource - must exist, or whether to tolerate its absence. If true - and the referenced resource is absent, proceed as if the - resource was present but empty, without any variables - defined. - type: boolean - required: - - kind - - name - type: object - type: array - type: object - prune: - description: Prune enables garbage collection. - type: boolean - retryInterval: - description: The interval at which to retry a previously failed reconciliation. - When not specified, the controller uses the KustomizationSpec.Interval - value to retry failures. - type: string - serviceAccountName: - description: The name of the Kubernetes service account to impersonate - when reconciling this Kustomization. - type: string - sourceRef: - description: Reference of the source where the kustomization file - is. - properties: - apiVersion: - description: API version of the referent. - type: string - kind: - description: Kind of the referent. - enum: - - OCIRepository - - GitRepository - - Bucket - type: string - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, defaults to the namespace - of the Kubernetes resource object that contains the reference. - type: string - required: - - kind - - name - type: object - suspend: - description: This flag tells the controller to suspend subsequent - kustomize executions, it does not apply to already started executions. - Defaults to false. - type: boolean - targetNamespace: - description: TargetNamespace sets or overrides the namespace in the - kustomization.yaml file. - maxLength: 63 - minLength: 1 - type: string - timeout: - description: Timeout for validation, apply and health checking operations. - Defaults to 'Interval' duration. - type: string - validation: - description: 'Deprecated: Not used in v1beta2.' - enum: - - none - - client - - server - type: string - wait: - description: Wait instructs the controller to check the health of - all the reconciled resources. When enabled, the HealthChecks are - ignored. Defaults to false. - type: boolean - required: - - interval - - prune - - sourceRef - type: object - status: - default: - observedGeneration: -1 - description: KustomizationStatus defines the observed state of a kustomization. - properties: - conditions: - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - inventory: - description: Inventory contains the list of Kubernetes resource object - references that have been successfully applied. - properties: - entries: - description: Entries of Kubernetes resource object references. - items: - description: ResourceRef contains the information necessary - to locate a resource within a cluster. - properties: - id: - description: ID is the string representation of the Kubernetes - resource object's metadata, in the format '___'. - type: string - v: - description: Version is the API version of the Kubernetes - resource object's kind. - type: string - required: - - id - - v - type: object - type: array - required: - - entries - type: object - lastAppliedRevision: - description: The last successfully applied revision. The revision - format for Git sources is /. - type: string - lastAttemptedRevision: - description: LastAttemptedRevision is the revision of the last reconciliation - attempt. - type: string - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last reconciled generation. - format: int64 - type: integer - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.7.0 - creationTimestamp: null - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - name: ocirepositories.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: OCIRepository - listKind: OCIRepositoryList - plural: ocirepositories - shortNames: - - ocirepo - singular: ocirepository - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta2 - schema: - openAPIV3Schema: - description: OCIRepository is the Schema for the ocirepositories API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: OCIRepositorySpec defines the desired state of OCIRepository - properties: - certSecretRef: - description: "CertSecretRef can be given the name of a secret containing - either or both of \n - a PEM-encoded client certificate (`certFile`) - and private key (`keyFile`); - a PEM-encoded CA certificate (`caFile`) - \n and whichever are supplied, will be used for connecting to the - \ registry. The client cert and key are useful if you are authenticating - with a certificate; the CA cert is useful if you are using a self-signed - server certificate." - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - ignore: - description: Ignore overrides the set of excluded patterns in the - .sourceignore format (which is the same as .gitignore). If not provided, - a default will be used, consult the documentation for your version - to find out what those are. - type: string - interval: - description: The interval at which to check for image updates. - type: string - layerSelector: - description: LayerSelector specifies which layer should be extracted - from the OCI artifact. When not specified, the first layer found - in the artifact is selected. - properties: - mediaType: - description: MediaType specifies the OCI media type of the layer - which should be extracted from the OCI Artifact. The first layer - matching this type is selected. - type: string - type: object - provider: - default: generic - description: The provider used for authentication, can be 'aws', 'azure', - 'gcp' or 'generic'. When not specified, defaults to 'generic'. - enum: - - generic - - aws - - azure - - gcp - type: string - ref: - description: The OCI reference to pull and monitor for changes, defaults - to the latest tag. - properties: - digest: - description: Digest is the image digest to pull, takes precedence - over SemVer. The value should be in the format 'sha256:'. - type: string - semver: - description: SemVer is the range of tags to pull selecting the - latest within the range, takes precedence over Tag. - type: string - tag: - description: Tag is the image tag to pull, defaults to latest. - type: string - type: object - secretRef: - description: SecretRef contains the secret name containing the registry - login credentials to resolve image metadata. The secret must be - of type kubernetes.io/dockerconfigjson. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - serviceAccountName: - description: 'ServiceAccountName is the name of the Kubernetes ServiceAccount - used to authenticate the image pull if the service account has attached - pull secrets. For more information: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account' - type: string - suspend: - description: This flag tells the controller to suspend the reconciliation - of this source. - type: boolean - timeout: - default: 60s - description: The timeout for remote OCI Repository operations like - pulling, defaults to 60s. - type: string - url: - description: URL is a reference to an OCI artifact repository hosted - on a remote container registry. - pattern: ^oci://.*$ - type: string - required: - - interval - - url - type: object - status: - default: - observedGeneration: -1 - description: OCIRepositoryStatus defines the observed state of OCIRepository - properties: - artifact: - description: Artifact represents the output of the last successful - OCI Repository sync. - properties: - checksum: - description: Checksum is the SHA256 checksum of the Artifact file. - type: string - lastUpdateTime: - description: LastUpdateTime is the timestamp corresponding to - the last update of the Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: Path is the relative file path of the Artifact. It - can be used to locate the file in the root of the Artifact storage - on the local file system of the controller managing the Source. - type: string - revision: - description: Revision is a human-readable identifier traceable - in the origin source system. It can be a Git commit SHA, Git - tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: URL is the HTTP address of the Artifact as exposed - by the controller managing the Source. It can be used to retrieve - the Artifact for consumption, e.g. by another controller applying - the Artifact contents. - type: string - required: - - path - - url - type: object - conditions: - description: Conditions holds the conditions for the OCIRepository. - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n \ttype FooStatus struct{ \t // Represents the observations - of a foo's current state. \t // Known .status.conditions.type - are: \"Available\", \"Progressing\", and \"Degraded\" \t // - +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map - \t // +listMapKey=type \t Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields - \t}" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value can - be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - url: - description: URL is the download link for the artifact output of the - last OCI Repository sync. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: [] - storedVersions: [] ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - name: kustomize-controller - namespace: flux-system ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - name: source-controller - namespace: flux-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - name: crd-controller-flux-system -rules: -- apiGroups: - - source.toolkit.fluxcd.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - kustomize.toolkit.fluxcd.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - helm.toolkit.fluxcd.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - notification.toolkit.fluxcd.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - image.toolkit.fluxcd.io - resources: - - '*' - verbs: - - '*' -- apiGroups: - - "" - resources: - - namespaces - - secrets - - configmaps - - serviceaccounts - verbs: - - get - - list - - watch -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - configmaps/status - verbs: - - get - - update - - patch -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - name: cluster-reconciler-flux-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: -- kind: ServiceAccount - name: kustomize-controller - namespace: flux-system -- kind: ServiceAccount - name: helm-controller - namespace: flux-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - name: crd-controller-flux-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: crd-controller-flux-system -subjects: -- kind: ServiceAccount - name: kustomize-controller - namespace: flux-system -- kind: ServiceAccount - name: helm-controller - namespace: flux-system -- kind: ServiceAccount - name: source-controller - namespace: flux-system -- kind: ServiceAccount - name: notification-controller - namespace: flux-system -- kind: ServiceAccount - name: image-reflector-controller - namespace: flux-system -- kind: ServiceAccount - name: image-automation-controller - namespace: flux-system ---- -apiVersion: v1 -kind: Service -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - control-plane: controller - name: source-controller - namespace: flux-system -spec: - ports: - - name: http - port: 80 - protocol: TCP - targetPort: http - selector: - app: source-controller - type: ClusterIP ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - control-plane: controller - name: kustomize-controller - namespace: flux-system -spec: - replicas: 1 - selector: - matchLabels: - app: kustomize-controller - template: - metadata: - annotations: - prometheus.io/port: "8080" - prometheus.io/scrape: "true" - labels: - app: kustomize-controller - spec: - containers: - - args: - - --events-addr= - - --watch-all-namespaces=true - - --log-level=info - - --log-encoding=json - - --enable-leader-election - env: - - name: RUNTIME_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - image: ghcr.io/fluxcd/kustomize-controller:v0.27.1 - imagePullPolicy: IfNotPresent - livenessProbe: - httpGet: - path: /healthz - port: healthz - name: manager - ports: - - containerPort: 8080 - name: http-prom - protocol: TCP - - containerPort: 9440 - name: healthz - protocol: TCP - readinessProbe: - httpGet: - path: /readyz - port: healthz - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 100m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - volumeMounts: - - mountPath: /tmp - name: temp - nodeSelector: - kubernetes.io/os: linux - securityContext: - fsGroup: 1337 - serviceAccountName: kustomize-controller - terminationGracePeriodSeconds: 60 - volumes: - - emptyDir: {} - name: temp ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - control-plane: controller - name: source-controller - namespace: flux-system -spec: - replicas: 1 - selector: - matchLabels: - app: source-controller - strategy: - type: Recreate - template: - metadata: - annotations: - prometheus.io/port: "8080" - prometheus.io/scrape: "true" - labels: - app: source-controller - spec: - containers: - - args: - - --events-addr= - - --watch-all-namespaces=true - - --log-level=info - - --log-encoding=json - - --enable-leader-election - - --storage-path=/data - - --storage-adv-addr=source-controller.$(RUNTIME_NAMESPACE).svc.cluster.local. - env: - - name: RUNTIME_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - image: ghcr.io/fluxcd/source-controller:v0.28.0 - imagePullPolicy: IfNotPresent - livenessProbe: - httpGet: - path: /healthz - port: healthz - name: manager - ports: - - containerPort: 9090 - name: http - protocol: TCP - - containerPort: 8080 - name: http-prom - protocol: TCP - - containerPort: 9440 - name: healthz - protocol: TCP - readinessProbe: - httpGet: - path: / - port: http - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 50m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - volumeMounts: - - mountPath: /data - name: data - - mountPath: /tmp - name: tmp - nodeSelector: - kubernetes.io/os: linux - securityContext: - fsGroup: 1337 - serviceAccountName: source-controller - terminationGracePeriodSeconds: 10 - volumes: - - emptyDir: {} - name: data - - emptyDir: {} - name: tmp ---- -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - name: allow-egress - namespace: flux-system -spec: - egress: - - {} - ingress: - - from: - - podSelector: {} - podSelector: {} - policyTypes: - - Ingress - - Egress ---- -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - name: allow-scraping - namespace: flux-system -spec: - ingress: - - from: - - namespaceSelector: {} - ports: - - port: 8080 - protocol: TCP - podSelector: {} - policyTypes: - - Ingress ---- -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - labels: - app.kubernetes.io/instance: flux-system - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v0.33.0 - name: allow-webhooks - namespace: flux-system -spec: - ingress: - - from: - - namespaceSelector: {} - podSelector: - matchLabels: - app: notification-controller - policyTypes: - - Ingress diff --git a/examples/podinfo-flux/podinfo-kustomization.yaml b/examples/podinfo-flux/git/podinfo-kustomization.yaml similarity index 81% rename from examples/podinfo-flux/podinfo-kustomization.yaml rename to examples/podinfo-flux/git/podinfo-kustomization.yaml index db498ac16d..bc8d5d50ef 100644 --- a/examples/podinfo-flux/podinfo-kustomization.yaml +++ b/examples/podinfo-flux/git/podinfo-kustomization.yaml @@ -2,7 +2,7 @@ apiVersion: kustomize.toolkit.fluxcd.io/v1beta2 kind: Kustomization metadata: - name: podinfo + name: podinfo-git namespace: flux-system spec: interval: 5m0s @@ -11,4 +11,4 @@ spec: sourceRef: kind: GitRepository name: podinfo - targetNamespace: podinfo + targetNamespace: podinfo-git diff --git a/examples/podinfo-flux/podinfo-source.yaml b/examples/podinfo-flux/git/podinfo-source.yaml similarity index 95% rename from examples/podinfo-flux/podinfo-source.yaml rename to examples/podinfo-flux/git/podinfo-source.yaml index e42f85dce2..3b6351955c 100644 --- a/examples/podinfo-flux/podinfo-source.yaml +++ b/examples/podinfo-flux/git/podinfo-source.yaml @@ -7,6 +7,6 @@ metadata: spec: interval: 30s ref: - tag: 6.3.3 + tag: 6.4.0 # Currently the Zarf Agent can only mutate urls that are proper URIs (i.e. scheme://host/repo) url: https://github.com/stefanprodan/podinfo.git diff --git a/examples/podinfo-flux/helm/podinfo-helmrelease.yaml b/examples/podinfo-flux/helm/podinfo-helmrelease.yaml new file mode 100644 index 0000000000..f8cfaa9a95 --- /dev/null +++ b/examples/podinfo-flux/helm/podinfo-helmrelease.yaml @@ -0,0 +1,17 @@ +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: podinfo + namespace: flux-system +spec: + interval: 5m0s + releaseName: podinfo + chart: + spec: + chart: podinfo + version: '6.4.0' + sourceRef: + kind: HelmRepository + name: podinfo + interval: 5m0s + targetNamespace: podinfo-helm diff --git a/examples/podinfo-flux/helm/podinfo-source.yaml b/examples/podinfo-flux/helm/podinfo-source.yaml new file mode 100644 index 0000000000..9c6d62e005 --- /dev/null +++ b/examples/podinfo-flux/helm/podinfo-source.yaml @@ -0,0 +1,9 @@ +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: podinfo + namespace: flux-system +spec: + type: oci + interval: 30s + url: oci://ghcr.io/stefanprodan/charts diff --git a/examples/podinfo-flux/oci/podinfo-kustomization.yaml b/examples/podinfo-flux/oci/podinfo-kustomization.yaml new file mode 100644 index 0000000000..57f290e7b6 --- /dev/null +++ b/examples/podinfo-flux/oci/podinfo-kustomization.yaml @@ -0,0 +1,14 @@ +--- +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: podinfo-oci + namespace: flux-system +spec: + interval: 5m0s + path: ./ + prune: true + sourceRef: + kind: OCIRepository + name: podinfo + targetNamespace: podinfo-oci diff --git a/examples/podinfo-flux/oci/podinfo-source.yaml b/examples/podinfo-flux/oci/podinfo-source.yaml new file mode 100644 index 0000000000..180659f63f --- /dev/null +++ b/examples/podinfo-flux/oci/podinfo-source.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1beta2 +kind: OCIRepository +metadata: + name: podinfo + namespace: flux-system +spec: + interval: 30s + url: oci://ghcr.io/stefanprodan/manifests/podinfo + ref: + tag: 6.4.0 diff --git a/examples/podinfo-flux/zarf.yaml b/examples/podinfo-flux/zarf.yaml index b06283e46e..4ab41b360e 100644 --- a/examples/podinfo-flux/zarf.yaml +++ b/examples/podinfo-flux/zarf.yaml @@ -8,27 +8,79 @@ components: description: Installs the flux CRDs / controllers to use flux-based deployments in the cluster required: true manifests: - - name: flux-crds + - name: flux-install namespace: flux files: - - flux-install.yaml + - https://github.com/fluxcd/flux2/releases/download/v2.3.0/install.yaml images: - - ghcr.io/fluxcd/kustomize-controller:v0.27.1 - - ghcr.io/fluxcd/source-controller:v0.28.0 + - ghcr.io/fluxcd/helm-controller:v1.0.1 + - ghcr.io/fluxcd/image-automation-controller:v0.38.0 + - ghcr.io/fluxcd/image-reflector-controller:v0.32.0 + - ghcr.io/fluxcd/kustomize-controller:v1.3.0 + - ghcr.io/fluxcd/notification-controller:v1.3.0 + - ghcr.io/fluxcd/source-controller:v1.3.0 - - name: podinfo-via-flux - description: Example deployment via flux using the famous podinfo example + - name: podinfo-via-flux-git + description: Example deployment via flux (git) using the famous podinfo example required: true manifests: - - name: podinfo-via-flux - namespace: podinfo + - name: podinfo + namespace: podinfo-git files: - - podinfo-source.yaml - - podinfo-kustomization.yaml + - git/podinfo-source.yaml + - git/podinfo-kustomization.yaml repos: - https://github.com/stefanprodan/podinfo.git images: - - ghcr.io/stefanprodan/podinfo:6.3.3 + - ghcr.io/stefanprodan/podinfo:6.4.0 + actions: + onDeploy: + after: + - description: Podinfo pods to be ready via wait action + wait: + cluster: + kind: pod + name: app=podinfo + namespace: podinfo-git + condition: ready + + - name: podinfo-via-flux-helm + description: Example deployment via flux (helm oci) using the famous podinfo example + required: true + manifests: + - name: podinfo + namespace: podinfo-helm + files: + - helm/podinfo-source.yaml + - helm/podinfo-helmrelease.yaml + images: + - ghcr.io/stefanprodan/podinfo:6.4.0 + # Note: this is a helm OCI artifact rather than a container image + - ghcr.io/stefanprodan/charts/podinfo:6.4.0 + actions: + onDeploy: + after: + - description: Podinfo pods to be ready via wait action + wait: + cluster: + kind: pod + name: app.kubernetes.io/name=podinfo + namespace: podinfo-helm + condition: ready + + - name: podinfo-via-flux-oci + description: Example deployment via flux (native oci) using the famous podinfo example + required: true + manifests: + - name: podinfo + namespace: podinfo-oci + files: + - oci/podinfo-source.yaml + - oci/podinfo-kustomization.yaml + images: + - ghcr.io/stefanprodan/podinfo:6.4.0 + # Note: this is a flux kustomize OCI artifact rather than a container image + - ghcr.io/stefanprodan/manifests/podinfo:6.4.0 actions: onDeploy: after: @@ -38,14 +90,24 @@ components: cluster: kind: pod name: app=podinfo - namespace: podinfo + namespace: podinfo-oci condition: ready # YAML keys starting with `x-` are custom keys that are ignored by the Zarf CLI # The `x-mdx` key is used to render the markdown content for https://docs.zarf.dev/ref/examples x-mdx: | - This example demonstrates how to use flux with Zarf to deploy the `stefanprodan/podinfo` app using GitOps. + This example demonstrates how to use Flux with Zarf to deploy the `stefanprodan/podinfo` app using GitRepositories, HelmRepositories, and OCIRepositories. - It uses a vanilla configuration of flux with upstream containers. + It uses a vanilla configuration of Flux with upstream containers. To learn more about how Zarf handles `git` repositories, see the [Git Repositories section](/ref/components/#git-repositories) of the package components documentation. + + :::caution + + Only `type: oci` HelmRepositories are supported by the Zarf Agent. The `type` key requires a HelmRepository CRD version greater than v1beta1. + + The Zarf agent will only automatically add the `insecure` key if the internal registry is used. If you are using a http registry outside of the cluster you will need to manually add this key. + + Due to an upstream bug, HelmRepositories with an insecure registry must use IP address instead of a hostname. This is not an issue with the internal Zarf registry, which is always an IP address, but will cause Flux HelmRepositories to break if Zarf is using an external http registry with a hostname. + + ::: diff --git a/go.mod b/go.mod index 30abd8b0ca..6195416cbf 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/anchore/clio v0.0.0-20240408173007-3c4abf89e72f github.com/anchore/stereoscope v0.0.1 github.com/anchore/syft v0.100.0 + github.com/defenseunicorns/pkg/helpers v1.1.3 github.com/defenseunicorns/pkg/helpers/v2 v2.0.1 github.com/defenseunicorns/pkg/kubernetes v0.0.1 github.com/defenseunicorns/pkg/oci v1.0.1 @@ -20,8 +21,8 @@ require ( github.com/fairwindsops/pluto/v5 v5.18.4 github.com/fatih/color v1.16.0 github.com/fluxcd/helm-controller/api v0.37.4 - github.com/fluxcd/pkg/apis/meta v1.3.0 - github.com/fluxcd/source-controller/api v1.2.4 + github.com/fluxcd/pkg/apis/meta v1.5.0 + github.com/fluxcd/source-controller/api v1.3.0 github.com/go-git/go-git/v5 v5.11.0 github.com/goccy/go-yaml v1.11.3 github.com/gofrs/flock v0.8.1 @@ -49,10 +50,10 @@ require ( golang.org/x/sync v0.7.0 golang.org/x/term v0.21.0 helm.sh/helm/v3 v3.14.2 - k8s.io/api v0.29.1 - k8s.io/apimachinery v0.29.1 - k8s.io/client-go v0.29.1 - k8s.io/component-base v0.29.1 + k8s.io/api v0.30.0 + k8s.io/apimachinery v0.30.0 + k8s.io/client-go v0.30.0 + k8s.io/component-base v0.30.0 k8s.io/klog/v2 v2.120.1 k8s.io/kubectl v0.29.1 oras.land/oras-go/v2 v2.5.0 @@ -222,13 +223,13 @@ require ( github.com/emicklei/proto v1.12.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/evanphx/json-patch v5.7.0+incompatible // indirect - github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect github.com/facebookincubator/nvdtools v0.1.5 // indirect github.com/fatih/camelcase v1.0.0 // indirect github.com/felixge/fgprof v0.9.3 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fluxcd/pkg/apis/acl v0.1.0 // indirect + github.com/fluxcd/pkg/apis/acl v0.3.0 // indirect github.com/fluxcd/pkg/apis/kustomize v1.3.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fvbommel/sortorder v1.1.0 // indirect @@ -500,11 +501,11 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/gorm v1.25.5 // indirect - k8s.io/apiextensions-apiserver v0.29.0 // indirect - k8s.io/apiserver v0.29.0 // indirect + k8s.io/apiextensions-apiserver v0.30.0 // indirect + k8s.io/apiserver v0.30.0 // indirect k8s.io/cli-runtime v0.29.1 // indirect k8s.io/component-helpers v0.29.1 // indirect - k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect k8s.io/metrics v0.29.1 // indirect k8s.io/utils v0.0.0-20231127182322-b307cd553661 // indirect modernc.org/libc v1.29.0 // indirect @@ -512,7 +513,7 @@ require ( modernc.org/memory v1.7.2 // indirect modernc.org/sqlite v1.28.0 // indirect oras.land/oras-go v1.2.4 // indirect - sigs.k8s.io/controller-runtime v0.16.3 // indirect + sigs.k8s.io/controller-runtime v0.18.1 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/kustomize/v5 v5.0.4-0.20230601165947-6ce0bf390ce3 // indirect sigs.k8s.io/release-utils v0.7.7 // indirect diff --git a/go.sum b/go.sum index 7cc3787141..2cdb4bd07f 100644 --- a/go.sum +++ b/go.sum @@ -597,6 +597,8 @@ github.com/daviddengcn/go-colortext v1.0.0 h1:ANqDyC0ys6qCSvuEK7l3g5RaehL/Xck9EX github.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c= github.com/defenseunicorns/gojsonschema v0.0.0-20231116163348-e00f069122d6 h1:gwevOZ0fxT2nzM9hrtdPbsiOHjFqDRIYMzJHba3/G6Q= github.com/defenseunicorns/gojsonschema v0.0.0-20231116163348-e00f069122d6/go.mod h1:StKLYMmPj1R5yIs6CK49EkcW1TvUYuw5Vri+LRk7Dy8= +github.com/defenseunicorns/pkg/helpers v1.1.3 h1:EVVuniq02qfAouR//AT0eoCngLWfFORj8H6+pI8M7uo= +github.com/defenseunicorns/pkg/helpers v1.1.3/go.mod h1:F4S5VZLDrlNWQKklzv4v9tFWjjZNhxJ1gT79j4XiLwk= github.com/defenseunicorns/pkg/helpers/v2 v2.0.1 h1:j08rz9vhyD9Bs+yKiyQMY2tSSejXRMxTqEObZ5M1Wbk= github.com/defenseunicorns/pkg/helpers/v2 v2.0.1/go.mod h1:u1PAqOICZyiGIVA2v28g55bQH1GiAt0Bc4U9/rnWQvQ= github.com/defenseunicorns/pkg/kubernetes v0.0.1 h1:HNQBV6XXFvlDvFdOCCWam0/LCgq67M+ggQKiRIoM2vU= @@ -684,8 +686,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= -github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= github.com/facebookincubator/flog v0.0.0-20190930132826-d2511d0ce33c/go.mod h1:QGzNH9ujQ2ZUr/CjDGZGWeDAVStrWNjHeEcjJL96Nuk= @@ -708,14 +710,14 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fluxcd/helm-controller/api v0.37.4 h1:rkBMqYXexyf1s5BS8QpxGi691DsCi+yugIFCM5fNKLU= github.com/fluxcd/helm-controller/api v0.37.4/go.mod h1:KFdP5Lbrc4Vv+Jt4xRj6UUo3qiwdBqBPl1xiiAnBe9c= -github.com/fluxcd/pkg/apis/acl v0.1.0 h1:EoAl377hDQYL3WqanWCdifauXqXbMyFuK82NnX6pH4Q= -github.com/fluxcd/pkg/apis/acl v0.1.0/go.mod h1:zfEZzz169Oap034EsDhmCAGgnWlcWmIObZjYMusoXS8= +github.com/fluxcd/pkg/apis/acl v0.3.0 h1:UOrKkBTOJK+OlZX7n8rWt2rdBmDCoTK+f5TY2LcZi8A= +github.com/fluxcd/pkg/apis/acl v0.3.0/go.mod h1:WVF9XjSMVBZuU+HTTiSebGAWMgM7IYexFLyVWbK9bNY= github.com/fluxcd/pkg/apis/kustomize v1.3.0 h1:qvB46CfaOWcL1SyR2RiVWN/j7/035D0OtB1ltLN7rgI= github.com/fluxcd/pkg/apis/kustomize v1.3.0/go.mod h1:PCXf5kktTzNav0aH2Ns3jsowqwmA9xTcsrEOoPzx/K8= -github.com/fluxcd/pkg/apis/meta v1.3.0 h1:KxeEc6olmSZvQ5pBONPE4IKxyoWQbqTJF1X6K5nIXpU= -github.com/fluxcd/pkg/apis/meta v1.3.0/go.mod h1:3Ui8xFkoU4sYehqmscjpq7NjqH2YN1A2iX2okbO3/yA= -github.com/fluxcd/source-controller/api v1.2.4 h1:XjKTWhSSeLGsogWnTcLl5sUnyMlC5TKDbbBgP9SyJ5c= -github.com/fluxcd/source-controller/api v1.2.4/go.mod h1:j3QSHpIPBP5sjaGIkVtsgWCx8JcOmcsutRmdJmRMOZg= +github.com/fluxcd/pkg/apis/meta v1.5.0 h1:/G82d2Az5D9op3F+wJUpD8jw/eTV0suM6P7+cSURoUM= +github.com/fluxcd/pkg/apis/meta v1.5.0/go.mod h1:Y3u7JomuuKtr5fvP1Iji2/50FdRe5GcBug2jawNVkdM= +github.com/fluxcd/source-controller/api v1.3.0 h1:Z5Lq0aJY87yg0cQDEuwGLKS60GhdErCHtsi546HUt10= +github.com/fluxcd/source-controller/api v1.3.0/go.mod h1:+tfd0vltjcVs/bbnq9AlYR9AAHSVfM/Z4v4TpQmdJf4= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= @@ -779,8 +781,8 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= -github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/analysis v0.22.0 h1:wQ/d07nf78HNj4u+KiSY0sT234IAyePPbMgpUjUJQR0= github.com/go-openapi/analysis v0.22.0/go.mod h1:acDnkkCI2QxIo8sSIPgmp1wUlRohV7vfGtAIVae73b0= github.com/go-openapi/errors v0.21.0 h1:FhChC/duCnfoLj1gZ0BgaBmzhJC2SL/sJr8a2vAobSY= @@ -1111,7 +1113,6 @@ github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E= github.com/jellydator/ttlcache/v3 v3.1.1 h1:RCgYJqo3jgvhl+fEWvjNW8thxGWsgxi+TPhRir1Y9y8= github.com/jellydator/ttlcache/v3 v3.1.1/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= @@ -1362,14 +1363,14 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= -github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= +github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= -github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= +github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= github.com/open-policy-agent/opa v0.61.0 h1:nhncQ2CAYtQTV/SMBhDDPsCpCQsUW+zO/1j+T5V7oZg= github.com/open-policy-agent/opa v0.61.0/go.mod h1:7OUuzJnsS9yHf8lw0ApfcbrnaRG1EkN3J2fuuqi4G/E= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -2181,8 +2182,6 @@ golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNq golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= -gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -2461,26 +2460,26 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.29.1 h1:DAjwWX/9YT7NQD4INu49ROJuZAAAP/Ijki48GUPzxqw= -k8s.io/api v0.29.1/go.mod h1:7Kl10vBRUXhnQQI8YR/R327zXC8eJ7887/+Ybta+RoQ= -k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3Q4lV0= -k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc= -k8s.io/apimachinery v0.29.1 h1:KY4/E6km/wLBguvCZv8cKTeOwwOBqFNjwJIdMkMbbRc= -k8s.io/apimachinery v0.29.1/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= -k8s.io/apiserver v0.29.0 h1:Y1xEMjJkP+BIi0GSEv1BBrf1jLU9UPfAnnGGbbDdp7o= -k8s.io/apiserver v0.29.0/go.mod h1:31n78PsRKPmfpee7/l9NYEv67u6hOL6AfcE761HapDM= +k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA= +k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE= +k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs= +k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y= +k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA= +k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/apiserver v0.30.0 h1:QCec+U72tMQ+9tR6A0sMBB5Vh6ImCEkoKkTDRABWq6M= +k8s.io/apiserver v0.30.0/go.mod h1:smOIBq8t0MbKZi7O7SyIpjPsiKJ8qa+llcFCluKyqiY= k8s.io/cli-runtime v0.29.1 h1:By3WVOlEWYfyxhGko0f/IuAOLQcbBSMzwSaDren2JUs= k8s.io/cli-runtime v0.29.1/go.mod h1:vjEY9slFp8j8UoMhV5AlO8uulX9xk6ogfIesHobyBDU= -k8s.io/client-go v0.29.1 h1:19B/+2NGEwnFLzt0uB5kNJnfTsbV8w6TgQRz9l7ti7A= -k8s.io/client-go v0.29.1/go.mod h1:TDG/psL9hdet0TI9mGyHJSgRkW3H9JZk2dNEUS7bRks= -k8s.io/component-base v0.29.1 h1:MUimqJPCRnnHsskTTjKD+IC1EHBbRCVyi37IoFBrkYw= -k8s.io/component-base v0.29.1/go.mod h1:fP9GFjxYrLERq1GcWWZAE3bqbNcDKDytn2srWuHTtKc= +k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ= +k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY= +k8s.io/component-base v0.30.0 h1:cj6bp38g0ainlfYtaOQuRELh5KSYjhKxM+io7AUIk4o= +k8s.io/component-base v0.30.0/go.mod h1:V9x/0ePFNaKeKYA3bOvIbrNoluTSG+fSJKjLdjOoeXQ= k8s.io/component-helpers v0.29.1 h1:54MMEDu6xeJmMtAKztsPwu0kJKr4+jCUzaEIn2UXRoc= k8s.io/component-helpers v0.29.1/go.mod h1:+I7xz4kfUgxWAPJIVKrqe4ml4rb9UGpazlOmhXYo+cY= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= -k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/kubectl v0.29.1 h1:rWnW3hi/rEUvvg7jp4iYB68qW5un/urKbv7fu3Vj0/s= k8s.io/kubectl v0.29.1/go.mod h1:SZzvLqtuOJYSvZzPZR9weSuP0wDQ+N37CENJf0FhDF4= k8s.io/metrics v0.29.1 h1:qutc3aIPMCniMuEApuLaeYX47rdCn8eycVDx7R6wMlQ= @@ -2504,8 +2503,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/cli-utils v0.36.0 h1:k7GM6LmIMydtvM6Ad91XuqKk0QEVL9bVbaiX1uvWIrA= sigs.k8s.io/cli-utils v0.36.0/go.mod h1:uCFC3BPXB3xHFQyKkWUlTrncVDCKzbdDfqZqRTCrk24= -sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= -sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= +sigs.k8s.io/controller-runtime v0.18.1 h1:RpWbigmuiylbxOCLy0tGnq1cU1qWPwNIQzoJk+QeJx4= +sigs.k8s.io/controller-runtime v0.18.1/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kustomize/api v0.16.0 h1:/zAR4FOQDCkgSDmVzV2uiFbuy9bhu3jEzthrHCuvm1g= diff --git a/packages/zarf-agent/manifests/clusterrole.yaml b/packages/zarf-agent/manifests/clusterrole.yaml new file mode 100644 index 0000000000..d28365447a --- /dev/null +++ b/packages/zarf-agent/manifests/clusterrole.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: service-viewer +rules: +- apiGroups: + - "" + resources: + - services + verbs: + - get + - list diff --git a/packages/zarf-agent/manifests/clusterrolebinding.yaml b/packages/zarf-agent/manifests/clusterrolebinding.yaml new file mode 100644 index 0000000000..fbfb53e3b0 --- /dev/null +++ b/packages/zarf-agent/manifests/clusterrolebinding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: service-viewer-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: service-viewer +subjects: +- kind: ServiceAccount + name: zarf + namespace: zarf diff --git a/packages/zarf-agent/manifests/webhook.yaml b/packages/zarf-agent/manifests/webhook.yaml index 136590c5d4..afe62fd0b2 100644 --- a/packages/zarf-agent/manifests/webhook.yaml +++ b/packages/zarf-agent/manifests/webhook.yaml @@ -48,6 +48,92 @@ webhooks: - "v1" - "v1beta1" sideEffects: None + - name: agent-flux-ocirepo.zarf.dev + namespaceSelector: + matchExpressions: + # Ensure we don't mess with kube-system + - key: "kubernetes.io/metadata.name" + operator: NotIn + values: + - "kube-system" + # Allow ignoring whole namespaces + - key: zarf.dev/agent + operator: NotIn + values: + - "skip" + - "ignore" + objectSelector: + matchExpressions: + # Always ignore specific resources if requested by annotation/label + - key: zarf.dev/agent + operator: NotIn + values: + - "skip" + - "ignore" + clientConfig: + service: + name: agent-hook + namespace: zarf + path: "/mutate/flux-ocirepository" + caBundle: "###ZARF_AGENT_CA###" + rules: + - operations: + - "CREATE" + - "UPDATE" + apiGroups: + - "source.toolkit.fluxcd.io" + apiVersions: + - "*" + resources: + - "ocirepositories" + admissionReviewVersions: + - "v1" + - "v1beta1" + sideEffects: None + - name: agent-flux-helmrepo.zarf.dev + namespaceSelector: + matchExpressions: + # Ensure we don't mess with kube-system + - key: "kubernetes.io/metadata.name" + operator: NotIn + values: + - "kube-system" + # Allow ignoring whole namespaces + - key: zarf.dev/agent + operator: NotIn + values: + - "skip" + - "ignore" + objectSelector: + matchExpressions: + # Always ignore specific resources if requested by annotation/label + - key: zarf.dev/agent + operator: NotIn + values: + - "skip" + - "ignore" + clientConfig: + service: + name: agent-hook + namespace: zarf + path: "/mutate/flux-helmrepository" + caBundle: "###ZARF_AGENT_CA###" + rules: + - operations: + - "CREATE" + - "UPDATE" + apiGroups: + - "source.toolkit.fluxcd.io" + apiVersions: + # While v1beta1 doesn't have the `type: oci` and is unsupported we still want to run mutations + # so that we can show a warning in the logs + - "*" + resources: + - "helmrepositories" + admissionReviewVersions: + - "v1" + - "v1beta1" + sideEffects: None - name: agent-flux-gitrepo.zarf.dev namespaceSelector: matchExpressions: diff --git a/packages/zarf-agent/zarf.yaml b/packages/zarf-agent/zarf.yaml index 32a6c34997..65ee63170f 100644 --- a/packages/zarf-agent/zarf.yaml +++ b/packages/zarf-agent/zarf.yaml @@ -29,6 +29,8 @@ components: - manifests/webhook.yaml - manifests/role.yaml - manifests/rolebinding.yaml + - manifests/clusterrole.yaml + - manifests/clusterrolebinding.yaml - manifests/serviceaccount.yaml actions: onCreate: diff --git a/site/src/content/docs/ref/actions.mdx b/site/src/content/docs/ref/actions.mdx index ae1c0c0fbc..c65cd75df1 100644 --- a/site/src/content/docs/ref/actions.mdx +++ b/site/src/content/docs/ref/actions.mdx @@ -203,7 +203,7 @@ The below example shows using a `wait` command to wait for a GitOps deployment t diff --git a/site/src/content/docs/ref/components.mdx b/site/src/content/docs/ref/components.mdx index ed2e8c884e..b8f85d6fc6 100644 --- a/site/src/content/docs/ref/components.mdx +++ b/site/src/content/docs/ref/components.mdx @@ -129,7 +129,7 @@ Kustomizations are handled a bit differently than normal manifests in that Zarf -Images can either be discovered manually, or automatically by using [`zarf dev find-images`](/commands/zarf_dev_find-images/). +Images can either be discovered manually, or automatically by using [`zarf dev find-images`](/commands/zarf_dev_find-images/). The image list is not limited to containers, any OCI image following the [Image Manifest specification](https://github.com/opencontainers/image-spec/blob/main/manifest.md) can be pulled :::note diff --git a/site/src/content/docs/ref/init-package.mdx b/site/src/content/docs/ref/init-package.mdx index 8bf4c8506b..8a8daa55de 100644 --- a/site/src/content/docs/ref/init-package.mdx +++ b/site/src/content/docs/ref/init-package.mdx @@ -151,13 +151,21 @@ Notably, the `REGISTRY_AFFINITY_CUSTOM` variable overrides the default pod anti- The `zarf-agent` is a [Kubernetes Mutating Webhook](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#mutatingadmissionwebhook) that intercepts requests to create resources and uses the `zarf-state` secret to mutate them to point to their air-gapped equivalents. -The `zarf-agent` is responsible for modifying [Kubernetes PodSpec](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#PodSpec) objects [Image](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#Container.Image) fields to point to the Zarf Registry. +The `zarf-agent` is responsible for modifying [Kubernetes PodSpec](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#PodSpec) objects [Image](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#Container.Image) fields to point to the Zarf Registry. This allows the cluster to pull images from the Zarf Registry instead of the internet without having to modify the original image references. -This allows the cluster to pull images from the Zarf Registry instead of the internet without having to modify the original image references. +The `zarf-agent` modifies the following [flux](https://fluxcd.io/flux/) resources: [GitRepository](https://fluxcd.io/docs/components/source/gitrepositories/), [OCIRepository](https://fluxcd.io/flux/components/source/ocirepositories/), & [HelmRepository](https://fluxcd.io/flux/components/source/helmrepositories/) to point to the local Git Server or Zarf Registry. HelmRepositories are only modified if the `type` key is set to `oci`. -The `zarf-agent` also modifies [Flux GitRepository](https://fluxcd.io/docs/components/source/gitrepositories/) objects to point to the local Git Server. +> Support for mutating OCIRepository and HelmRepository objects is in [`alpha`](/roadmap#alpha) and should be tested on non-production clusters before being deployed to production clusters. -> Support for mutating `Application` and `Repository` objects in ArgoCD is in [`beta`](/roadmap#beta) and should tested on non-production clusters before being deployed to production clusters. +:::caution + +Due to a bug in helm, HelmRepositories with an insecure registry must use IP address instead of a hostname. This is not an issue with the internal Zarf registry, which is always an IP address, but will cause Flux HelmRepositories to break if Zarf is using an external http registry with a hostname. + +::: + +The `zarf-agent` modifies [ArgoCD applications](https://argo-cd.readthedocs.io/en/stable/user-guide/application-specification/) & [ArgoCD Repositories](https://argo-cd.readthedocs.io/en/stable/user-guide/private-repositories/) objects to point to the local Git Server. + +> Support for mutating `Application` and `Repository` objects in ArgoCD is in [`beta`](/roadmap#beta) and should be tested on non-production clusters before being deployed to production clusters. :::note diff --git a/src/config/lang/english.go b/src/config/lang/english.go index 68caf91270..d4f2e4d4d3 100644 --- a/src/config/lang/english.go +++ b/src/config/lang/english.go @@ -642,9 +642,10 @@ $ zarf tools update-creds artifact --artifact-push-username={USERNAME} --artifac // Zarf Agent messages // These are only seen in the Kubernetes logs. const ( - AgentInfoWebhookAllowed = "Webhook [%s - %s] - Allowed: %t" - AgentInfoPort = "Server running in port: %s" - + AgentInfoWebhookAllowed = "Webhook [%s - %s] - Allowed: %t" + AgentInfoPort = "Server running in port: %s" + AgentWarnNotOCIType = "Skipping HelmRepo mutation because the type is not OCI: %s" + AgentWarnSemVerRef = "Detected a semver OCI ref (%s) - continuing but will be unable to guarantee against collisions if multiple OCI artifacts with the same name are brought in from different registries" AgentErrBadRequest = "could not read request body: %s" AgentErrBindHandler = "Unable to bind the webhook handler" AgentErrCouldNotDeserializeReq = "could not deserialize request: %s" diff --git a/src/internal/agent/hooks/flux.go b/src/internal/agent/hooks/flux-gitrepo.go similarity index 100% rename from src/internal/agent/hooks/flux.go rename to src/internal/agent/hooks/flux-gitrepo.go diff --git a/src/internal/agent/hooks/flux_test.go b/src/internal/agent/hooks/flux-gitrepo_test.go similarity index 100% rename from src/internal/agent/hooks/flux_test.go rename to src/internal/agent/hooks/flux-gitrepo_test.go diff --git a/src/internal/agent/hooks/flux-helmrepo.go b/src/internal/agent/hooks/flux-helmrepo.go new file mode 100644 index 0000000000..f4514b639d --- /dev/null +++ b/src/internal/agent/hooks/flux-helmrepo.go @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +// Package hooks contains the mutation hooks for the Zarf agent. +package hooks + +import ( + "context" + "encoding/json" + "fmt" + "strings" + + "github.com/defenseunicorns/pkg/helpers" + "github.com/defenseunicorns/zarf/src/config" + "github.com/defenseunicorns/zarf/src/config/lang" + "github.com/defenseunicorns/zarf/src/internal/agent/operations" + "github.com/defenseunicorns/zarf/src/pkg/cluster" + "github.com/defenseunicorns/zarf/src/pkg/message" + "github.com/defenseunicorns/zarf/src/pkg/transform" + "github.com/fluxcd/pkg/apis/meta" + flux "github.com/fluxcd/source-controller/api/v1" + v1 "k8s.io/api/admission/v1" +) + +// NewHelmRepositoryMutationHook creates a new instance of the helm repo mutation hook. +func NewHelmRepositoryMutationHook(ctx context.Context, cluster *cluster.Cluster) operations.Hook { + message.Debug("hooks.NewHelmRepositoryMutationHook()") + return operations.Hook{ + Create: func(r *v1.AdmissionRequest) (*operations.Result, error) { + return mutateHelmRepo(ctx, r, cluster) + }, + Update: func(r *v1.AdmissionRequest) (*operations.Result, error) { + return mutateHelmRepo(ctx, r, cluster) + }, + } +} + +// mutateHelmRepo mutates the repository url to point to the repository URL defined in the ZarfState. +func mutateHelmRepo(ctx context.Context, r *v1.AdmissionRequest, cluster *cluster.Cluster) (*operations.Result, error) { + + src := &flux.HelmRepository{} + if err := json.Unmarshal(r.Object.Raw, &src); err != nil { + return nil, fmt.Errorf(lang.ErrUnmarshal, err) + } + + // If we see a type of helm repo other than OCI we should flag a warning and return + if strings.ToLower(src.Spec.Type) != "oci" { + message.Warnf(lang.AgentWarnNotOCIType, src.Spec.Type) + return &operations.Result{Allowed: true}, nil + } + + if src.Labels != nil && src.Labels["zarf-agent"] == "patched" { + return &operations.Result{ + Allowed: true, + PatchOps: nil, + }, nil + } + + zarfState, err := cluster.LoadZarfState(ctx) + if err != nil { + return nil, fmt.Errorf(lang.AgentErrGetState, err) + } + + // Get the registry service info if this is a NodePort service to use the internal kube-dns + registryAddress, err := cluster.GetServiceInfoFromRegistryAddress(ctx, zarfState.RegistryInfo.Address) + if err != nil { + return nil, err + } + + message.Debugf("Using the url of (%s) to mutate the flux HelmRepository", registryAddress) + + patchedSrc, err := transform.ImageTransformHost(registryAddress, src.Spec.URL) + if err != nil { + return nil, fmt.Errorf("unable to transform the HelmRepo URL: %w", err) + } + + patchedRefInfo, err := transform.ParseImageRef(patchedSrc) + if err != nil { + return nil, fmt.Errorf("unable to parse the HelmRepo URL: %w", err) + } + patchedURL := helpers.OCIURLPrefix + patchedRefInfo.Name + + message.Debugf("original HelmRepo URL of (%s) got mutated to (%s)", src.Spec.URL, patchedURL) + + patches := populateHelmRepoPatchOperations(patchedURL, zarfState.RegistryInfo.InternalRegistry) + + patches = append(patches, getLabelPatch(src.Labels)) + + return &operations.Result{ + Allowed: true, + PatchOps: patches, + }, nil +} + +func populateHelmRepoPatchOperations(repoURL string, isInternal bool) []operations.PatchOperation { + var patches []operations.PatchOperation + patches = append(patches, operations.ReplacePatchOperation("/spec/url", repoURL)) + + if isInternal { + patches = append(patches, operations.ReplacePatchOperation("/spec/insecure", true)) + } + + patches = append(patches, operations.AddPatchOperation("/spec/secretRef", meta.LocalObjectReference{Name: config.ZarfImagePullSecretName})) + + return patches +} diff --git a/src/internal/agent/hooks/flux-helmrepo_test.go b/src/internal/agent/hooks/flux-helmrepo_test.go new file mode 100644 index 0000000000..85895bd31b --- /dev/null +++ b/src/internal/agent/hooks/flux-helmrepo_test.go @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +package hooks + +import ( + "context" + "encoding/json" + "net/http" + "testing" + + "github.com/defenseunicorns/zarf/src/config" + "github.com/defenseunicorns/zarf/src/internal/agent/http/admission" + "github.com/defenseunicorns/zarf/src/internal/agent/operations" + "github.com/defenseunicorns/zarf/src/types" + fluxmeta "github.com/fluxcd/pkg/apis/meta" + flux "github.com/fluxcd/source-controller/api/v1" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/admission/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +func createFluxHelmRepoAdmissionRequest(t *testing.T, op v1.Operation, fluxHelmRepo *flux.HelmRepository) *v1.AdmissionRequest { + t.Helper() + raw, err := json.Marshal(fluxHelmRepo) + require.NoError(t, err) + return &v1.AdmissionRequest{ + Operation: op, + Object: runtime.RawExtension{ + Raw: raw, + }, + } +} + +func TestFluxHelmMutationWebhook(t *testing.T) { + t.Parallel() + + ctx := context.Background() + state := &types.ZarfState{RegistryInfo: types.RegistryInfo{Address: "127.0.0.1:31999"}} + + tests := []admissionTest{ + { + name: "should not mutate when type is not oci", + admissionReq: createFluxHelmRepoAdmissionRequest(t, v1.Update, &flux.HelmRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: "not-oci", + }, + Spec: flux.HelmRepositorySpec{ + Type: "default", + }, + }), + code: http.StatusOK, + }, + { + name: "error on bad url", + admissionReq: createFluxHelmRepoAdmissionRequest(t, v1.Update, &flux.HelmRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bad-url", + }, + Spec: flux.HelmRepositorySpec{ + Type: "oci", + URL: "bad-url$", + }, + }), + errContains: "unable to transform the HelmRepo URL", + code: http.StatusInternalServerError, + }, + { + name: "should not mutate when agent patched", + admissionReq: createFluxHelmRepoAdmissionRequest(t, v1.Update, &flux.HelmRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: "already-patched", + Labels: map[string]string{ + "zarf-agent": "patched", + }, + }, + Spec: flux.HelmRepositorySpec{ + Type: "oci", + }, + }), + code: http.StatusOK, + }, + { + name: "should be mutated with no internal service registry", + admissionReq: createFluxHelmRepoAdmissionRequest(t, v1.Create, &flux.HelmRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mutate-this", + }, + Spec: flux.HelmRepositorySpec{ + URL: "oci://ghcr.io/stefanprodan/charts", + Type: "oci", + }, + }), + patch: []operations.PatchOperation{ + operations.ReplacePatchOperation( + "/spec/url", + "oci://127.0.0.1:31999/stefanprodan/charts", + ), + operations.AddPatchOperation( + "/spec/secretRef", + fluxmeta.LocalObjectReference{Name: config.ZarfImagePullSecretName}, + ), + operations.ReplacePatchOperation( + "/metadata/labels", + map[string]string{ + "zarf-agent": "patched", + }, + ), + }, + code: http.StatusOK, + }, + { + name: "should be mutated with internal service registry", + admissionReq: createFluxHelmRepoAdmissionRequest(t, v1.Create, &flux.HelmRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mutate-this", + }, + Spec: flux.HelmRepositorySpec{ + URL: "oci://ghcr.io/stefanprodan/charts", + Type: "oci", + }, + }), + patch: []operations.PatchOperation{ + operations.ReplacePatchOperation( + "/spec/url", + "oci://10.11.12.13:5000/stefanprodan/charts", + ), + operations.AddPatchOperation( + "/spec/secretRef", + fluxmeta.LocalObjectReference{Name: config.ZarfImagePullSecretName}, + ), + operations.ReplacePatchOperation( + "/metadata/labels", + map[string]string{ + "zarf-agent": "patched", + }, + ), + }, + svc: &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "zarf-docker-registry", + Namespace: "zarf", + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeNodePort, + Ports: []corev1.ServicePort{ + { + NodePort: int32(31999), + Port: 5000, + }, + }, + ClusterIP: "10.11.12.13", + }, + }, + code: http.StatusOK, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + c := createTestClientWithZarfState(ctx, t, state) + handler := admission.NewHandler().Serve(NewHelmRepositoryMutationHook(ctx, c)) + if tt.svc != nil { + _, err := c.Clientset.CoreV1().Services("zarf").Create(ctx, tt.svc, metav1.CreateOptions{}) + require.NoError(t, err) + } + rr := sendAdmissionRequest(t, tt.admissionReq, handler) + verifyAdmission(t, rr, tt) + }) + } +} diff --git a/src/internal/agent/hooks/flux-ocirepo.go b/src/internal/agent/hooks/flux-ocirepo.go new file mode 100644 index 0000000000..1a811c3319 --- /dev/null +++ b/src/internal/agent/hooks/flux-ocirepo.go @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +// Package hooks contains the mutation hooks for the Zarf agent. +package hooks + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/defenseunicorns/pkg/helpers" + "github.com/defenseunicorns/zarf/src/config" + "github.com/defenseunicorns/zarf/src/config/lang" + "github.com/defenseunicorns/zarf/src/internal/agent/operations" + "github.com/defenseunicorns/zarf/src/pkg/cluster" + "github.com/defenseunicorns/zarf/src/pkg/message" + "github.com/defenseunicorns/zarf/src/pkg/transform" + "github.com/fluxcd/pkg/apis/meta" + flux "github.com/fluxcd/source-controller/api/v1beta2" + v1 "k8s.io/api/admission/v1" +) + +// NewOCIRepositoryMutationHook creates a new instance of the oci repo mutation hook. +func NewOCIRepositoryMutationHook(ctx context.Context, cluster *cluster.Cluster) operations.Hook { + message.Debug("hooks.NewOCIRepositoryMutationHook()") + return operations.Hook{ + Create: func(r *v1.AdmissionRequest) (*operations.Result, error) { + return mutateOCIRepo(ctx, r, cluster) + }, + Update: func(r *v1.AdmissionRequest) (*operations.Result, error) { + return mutateOCIRepo(ctx, r, cluster) + }, + } +} + +// mutateOCIRepo mutates the oci repository url to point to the repository URL defined in the ZarfState. +func mutateOCIRepo(ctx context.Context, r *v1.AdmissionRequest, cluster *cluster.Cluster) (*operations.Result, error) { + + src := &flux.OCIRepository{} + if err := json.Unmarshal(r.Object.Raw, &src); err != nil { + return nil, fmt.Errorf(lang.ErrUnmarshal, err) + } + + if src.Spec.Reference == nil { + src.Spec.Reference = &flux.OCIRepositoryRef{} + } + + // If we have a semver we want to continue since we wil still have the upstream tag + // but should warn that we can't guarantee there won't be collisions + if src.Spec.Reference.SemVer != "" { + message.Warnf(lang.AgentWarnSemVerRef, src.Spec.Reference.SemVer) + } + + if src.Labels != nil && src.Labels["zarf-agent"] == "patched" { + return &operations.Result{ + Allowed: true, + PatchOps: []operations.PatchOperation{}, + }, nil + } + + zarfState, err := cluster.LoadZarfState(ctx) + if err != nil { + return nil, fmt.Errorf(lang.AgentErrGetState, err) + } + + // Get the registry service info if this is a NodePort service to use the internal kube-dns + registryAddress, err := cluster.GetServiceInfoFromRegistryAddress(ctx, zarfState.RegistryInfo.Address) + if err != nil { + return nil, err + } + + // For the internal registry this will be the ip & port of the service, it may look like 10.43.36.151:5000 + message.Debugf("Using the url of (%s) to mutate the flux OCIRepository", registryAddress) + + ref := src.Spec.URL + if src.Spec.Reference.Digest != "" { + ref = fmt.Sprintf("%s@%s", ref, src.Spec.Reference.Digest) + } else if src.Spec.Reference.Tag != "" { + ref = fmt.Sprintf("%s:%s", ref, src.Spec.Reference.Tag) + } + + patchedSrc, err := transform.ImageTransformHost(registryAddress, ref) + if err != nil { + return nil, fmt.Errorf("unable to transform the OCIRepo URL: %w", err) + } + + patchedRefInfo, err := transform.ParseImageRef(patchedSrc) + if err != nil { + return nil, fmt.Errorf("unable to parse the transformed OCIRepo URL: %w", err) + } + patchedRef := src.Spec.Reference + + patchedURL := helpers.OCIURLPrefix + patchedRefInfo.Name + + if patchedRefInfo.Digest != "" { + patchedRef.Digest = patchedRefInfo.Digest + } else if patchedRefInfo.Tag != "" { + patchedRef.Tag = patchedRefInfo.Tag + } + + message.Debugf("original OCIRepo URL of (%s) got mutated to (%s)", src.Spec.URL, patchedURL) + + patches := populateOCIRepoPatchOperations(patchedURL, zarfState.RegistryInfo.InternalRegistry, patchedRef) + + patches = append(patches, getLabelPatch(src.Labels)) + return &operations.Result{ + Allowed: true, + PatchOps: patches, + }, nil +} + +func populateOCIRepoPatchOperations(repoURL string, isInternal bool, ref *flux.OCIRepositoryRef) []operations.PatchOperation { + var patches []operations.PatchOperation + patches = append(patches, operations.ReplacePatchOperation("/spec/url", repoURL)) + + patches = append(patches, operations.AddPatchOperation("/spec/secretRef", meta.LocalObjectReference{Name: config.ZarfImagePullSecretName})) + + if isInternal { + patches = append(patches, operations.ReplacePatchOperation("/spec/insecure", true)) + } + + // If semver is used we don't want to add the ":latest" tag + crc to the spec + if ref.SemVer != "" { + return patches + } + + if ref.Tag != "" { + patches = append(patches, operations.ReplacePatchOperation("/spec/ref/tag", ref.Tag)) + } + + return patches +} diff --git a/src/internal/agent/hooks/flux-ocirepo_test.go b/src/internal/agent/hooks/flux-ocirepo_test.go new file mode 100644 index 0000000000..4b2ff6cf57 --- /dev/null +++ b/src/internal/agent/hooks/flux-ocirepo_test.go @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +package hooks + +import ( + "context" + "encoding/json" + "net/http" + "testing" + + "github.com/defenseunicorns/zarf/src/config" + "github.com/defenseunicorns/zarf/src/internal/agent/http/admission" + "github.com/defenseunicorns/zarf/src/internal/agent/operations" + "github.com/defenseunicorns/zarf/src/types" + fluxmeta "github.com/fluxcd/pkg/apis/meta" + flux "github.com/fluxcd/source-controller/api/v1beta2" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/admission/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +func createFluxOCIRepoAdmissionRequest(t *testing.T, op v1.Operation, fluxOCIRepo *flux.OCIRepository) *v1.AdmissionRequest { + t.Helper() + raw, err := json.Marshal(fluxOCIRepo) + require.NoError(t, err) + return &v1.AdmissionRequest{ + Operation: op, + Object: runtime.RawExtension{ + Raw: raw, + }, + } +} + +func TestFluxOCIMutationWebhook(t *testing.T) { + t.Parallel() + + tests := []admissionTest{ + { + name: "should not mutate when agent patched", + admissionReq: createFluxOCIRepoAdmissionRequest(t, v1.Update, &flux.OCIRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: "already-patched", + Labels: map[string]string{ + "zarf-agent": "patched", + }, + }, + Spec: flux.OCIRepositorySpec{ + URL: "oci://ghcr.io/stefanprodan/manifests/podinfo", + Reference: &flux.OCIRepositoryRef{ + Tag: "6.4.0", + }, + }, + }), + patch: nil, + code: http.StatusOK, + }, + { + name: "bad oci url", + admissionReq: createFluxOCIRepoAdmissionRequest(t, v1.Update, &flux.OCIRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bad oci url", + }, + Spec: flux.OCIRepositorySpec{ + URL: "bad://ghcr.io/$", + }, + }), + errContains: "unable to transform the OCIRepo URL", + code: http.StatusInternalServerError, + }, + { + name: "should be mutated with no internal service registry", + admissionReq: createFluxOCIRepoAdmissionRequest(t, v1.Update, &flux.OCIRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mutate-this", + }, + Spec: flux.OCIRepositorySpec{ + URL: "oci://ghcr.io/stefanprodan/manifests/podinfo", + Reference: &flux.OCIRepositoryRef{ + Tag: "6.4.0", + }, + }, + }), + patch: []operations.PatchOperation{ + operations.ReplacePatchOperation( + "/spec/url", + "oci://127.0.0.1:31999/stefanprodan/manifests/podinfo", + ), + operations.AddPatchOperation( + "/spec/secretRef", + fluxmeta.LocalObjectReference{Name: config.ZarfImagePullSecretName}, + ), + operations.ReplacePatchOperation( + "/spec/ref/tag", + "6.4.0-zarf-2823281104", + ), + operations.ReplacePatchOperation( + "/metadata/labels", + map[string]string{ + "zarf-agent": "patched", + }, + ), + }, + code: http.StatusOK, + }, + { + name: "test semver tag", + admissionReq: createFluxOCIRepoAdmissionRequest(t, v1.Update, &flux.OCIRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mutate-this", + }, + Spec: flux.OCIRepositorySpec{ + URL: "oci://ghcr.io/stefanprodan/manifests/podinfo", + Reference: &flux.OCIRepositoryRef{ + SemVer: ">= 6.4.0", + }, + }, + }), + patch: []operations.PatchOperation{ + operations.ReplacePatchOperation( + "/spec/url", + "oci://127.0.0.1:31999/stefanprodan/manifests/podinfo", + ), + operations.AddPatchOperation( + "/spec/secretRef", + fluxmeta.LocalObjectReference{Name: config.ZarfImagePullSecretName}, + ), + operations.ReplacePatchOperation( + "/metadata/labels", + map[string]string{ + "zarf-agent": "patched", + }, + ), + }, + code: http.StatusOK, + }, + { + name: "should be mutated with internal service registry", + admissionReq: createFluxOCIRepoAdmissionRequest(t, v1.Create, &flux.OCIRepository{ + ObjectMeta: metav1.ObjectMeta{ + Name: "mutate-this", + }, + Spec: flux.OCIRepositorySpec{ + URL: "oci://ghcr.io/stefanprodan/charts", + Reference: &flux.OCIRepositoryRef{ + Digest: "sha256:6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b", + }, + }, + }), + patch: []operations.PatchOperation{ + operations.ReplacePatchOperation( + "/spec/url", + "oci://10.11.12.13:5000/stefanprodan/charts", + ), + operations.AddPatchOperation( + "/spec/secretRef", + fluxmeta.LocalObjectReference{Name: config.ZarfImagePullSecretName}, + ), + operations.ReplacePatchOperation( + "/metadata/labels", + map[string]string{ + "zarf-agent": "patched", + }, + ), + }, + svc: &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "zarf-docker-registry", + Namespace: "zarf", + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeNodePort, + Ports: []corev1.ServicePort{ + { + NodePort: int32(31999), + Port: 5000, + }, + }, + ClusterIP: "10.11.12.13", + }, + }, + code: http.StatusOK, + }, + } + + ctx := context.Background() + state := &types.ZarfState{RegistryInfo: types.RegistryInfo{Address: "127.0.0.1:31999"}} + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + c := createTestClientWithZarfState(ctx, t, state) + handler := admission.NewHandler().Serve(NewOCIRepositoryMutationHook(ctx, c)) + if tt.svc != nil { + _, err := c.Clientset.CoreV1().Services("zarf").Create(ctx, tt.svc, metav1.CreateOptions{}) + require.NoError(t, err) + } + rr := sendAdmissionRequest(t, tt.admissionReq, handler) + verifyAdmission(t, rr, tt) + }) + } +} diff --git a/src/internal/agent/hooks/utils_test.go b/src/internal/agent/hooks/utils_test.go index 716a5b5245..f2d4ed6136 100644 --- a/src/internal/agent/hooks/utils_test.go +++ b/src/internal/agent/hooks/utils_test.go @@ -27,6 +27,7 @@ type admissionTest struct { patch []operations.PatchOperation code int errContains string + svc *corev1.Service } func createTestClientWithZarfState(ctx context.Context, t *testing.T, state *types.ZarfState) *cluster.Cluster { diff --git a/src/internal/agent/http/server.go b/src/internal/agent/http/server.go index 7938f35d6e..9bcff6a431 100644 --- a/src/internal/agent/http/server.go +++ b/src/internal/agent/http/server.go @@ -26,20 +26,24 @@ func NewAdmissionServer(ctx context.Context, port string) *http.Server { message.Fatalf(err, err.Error()) } - // Instances hooks + // Routers + admissionHandler := admission.NewHandler() podsMutation := hooks.NewPodMutationHook(ctx, c) fluxGitRepositoryMutation := hooks.NewGitRepositoryMutationHook(ctx, c) argocdApplicationMutation := hooks.NewApplicationMutationHook(ctx, c) argocdRepositoryMutation := hooks.NewRepositorySecretMutationHook(ctx, c) + fluxHelmRepositoryMutation := hooks.NewHelmRepositoryMutationHook(ctx, c) + fluxOCIRepositoryMutation := hooks.NewOCIRepositoryMutationHook(ctx, c) // Routers - ah := admission.NewHandler() mux := http.NewServeMux() mux.Handle("/healthz", healthz()) - mux.Handle("/mutate/pod", ah.Serve(podsMutation)) - mux.Handle("/mutate/flux-gitrepository", ah.Serve(fluxGitRepositoryMutation)) - mux.Handle("/mutate/argocd-application", ah.Serve(argocdApplicationMutation)) - mux.Handle("/mutate/argocd-repository", ah.Serve(argocdRepositoryMutation)) + mux.Handle("/mutate/pod", admissionHandler.Serve(podsMutation)) + mux.Handle("/mutate/flux-gitrepository", admissionHandler.Serve(fluxGitRepositoryMutation)) + mux.Handle("/mutate/flux-helmrepository", admissionHandler.Serve(fluxHelmRepositoryMutation)) + mux.Handle("/mutate/flux-ocirepository", admissionHandler.Serve(fluxOCIRepositoryMutation)) + mux.Handle("/mutate/argocd-application", admissionHandler.Serve(argocdApplicationMutation)) + mux.Handle("/mutate/argocd-repository", admissionHandler.Serve(argocdRepositoryMutation)) mux.Handle("/metrics", promhttp.Handler()) return &http.Server{ diff --git a/src/internal/packager/helm/post-render.go b/src/internal/packager/helm/post-render.go index 1dcdbc6912..43bc102a6b 100644 --- a/src/internal/packager/helm/post-render.go +++ b/src/internal/packager/helm/post-render.go @@ -159,8 +159,11 @@ func (r *renderer) adoptAndUpdateNamespaces(ctx context.Context) error { continue } - // Try to get a valid existing secret - validRegistrySecret := c.GenerateRegistryPullCreds(name, config.ZarfImagePullSecretName, r.state.RegistryInfo) + // Create the secret + validRegistrySecret, err := c.GenerateRegistryPullCreds(ctx, name, config.ZarfImagePullSecretName, r.state.RegistryInfo) + if err != nil { + return err + } // TODO: Refactor as error is not checked instead of checking for not found error. currentRegistrySecret, _ := c.Clientset.CoreV1().Secrets(name).Get(ctx, config.ZarfImagePullSecretName, metav1.GetOptions{}) sameSecretData := maps.EqualFunc(currentRegistrySecret.Data, validRegistrySecret.Data, func(v1, v2 []byte) bool { return bytes.Equal(v1, v2) }) diff --git a/src/pkg/cluster/secrets.go b/src/pkg/cluster/secrets.go index 2e5ff26ecc..09be8beba5 100644 --- a/src/pkg/cluster/secrets.go +++ b/src/pkg/cluster/secrets.go @@ -9,6 +9,7 @@ import ( "context" "encoding/base64" "encoding/json" + "fmt" "maps" corev1 "k8s.io/api/core/v1" @@ -33,25 +34,37 @@ type DockerConfigEntryWithAuth struct { } // GenerateRegistryPullCreds generates a secret containing the registry credentials. -func (c *Cluster) GenerateRegistryPullCreds(namespace, name string, registryInfo types.RegistryInfo) *corev1.Secret { +func (c *Cluster) GenerateRegistryPullCreds(ctx context.Context, namespace, name string, registryInfo types.RegistryInfo) (*corev1.Secret, error) { // Auth field must be username:password and base64 encoded fieldValue := registryInfo.PullUsername + ":" + registryInfo.PullPassword authEncodedValue := base64.StdEncoding.EncodeToString([]byte(fieldValue)) - registry := registryInfo.Address - // Create the expected structure for the dockerconfigjson dockerConfigJSON := DockerConfig{ Auths: DockerConfigEntry{ - registry: DockerConfigEntryWithAuth{ + // nodePort for zarf-docker-registry + registryInfo.Address: DockerConfigEntryWithAuth{ Auth: authEncodedValue, }, }, } + serviceList, err := c.Clientset.CoreV1().Services("").List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, err + } + // Build zarf-docker-registry service address string + svc, port, err := serviceInfoFromNodePortURL(serviceList.Items, registryInfo.Address) + if err == nil { + kubeDNSRegistryURL := fmt.Sprintf("%s:%d", svc.Spec.ClusterIP, port) + dockerConfigJSON.Auths[kubeDNSRegistryURL] = DockerConfigEntryWithAuth{ + Auth: authEncodedValue, + } + } + // Convert to JSON dockerConfigData, err := json.Marshal(dockerConfigJSON) if err != nil { - message.WarnErrf(err, "Unable to marshal the .dockerconfigjson secret data for the image pull secret") + return nil, fmt.Errorf("Unable to marshal the .dockerconfigjson secret data for the image pull secret: %w", err) } secretDockerConfig := &corev1.Secret{ @@ -71,7 +84,7 @@ func (c *Cluster) GenerateRegistryPullCreds(namespace, name string, registryInfo ".dockerconfigjson": dockerConfigData, }, } - return secretDockerConfig + return secretDockerConfig, nil } // GenerateGitPullCreds generates a secret containing the git credentials. @@ -122,7 +135,11 @@ func (c *Cluster) UpdateZarfManagedImageSecrets(ctx context.Context, state *type (namespace.Labels[AgentLabel] != "skip" && namespace.Labels[AgentLabel] != "ignore") { spinner.Updatef("Updating existing Zarf-managed image secret for namespace: '%s'", namespace.Name) - newRegistrySecret := c.GenerateRegistryPullCreds(namespace.Name, config.ZarfImagePullSecretName, state.RegistryInfo) + newRegistrySecret, err := c.GenerateRegistryPullCreds(ctx, namespace.Name, config.ZarfImagePullSecretName, state.RegistryInfo) + if err != nil { + message.WarnErrf(err, "Unable to generate registry creds") + continue + } if !maps.EqualFunc(currentRegistrySecret.Data, newRegistrySecret.Data, func(v1, v2 []byte) bool { return bytes.Equal(v1, v2) }) { _, err := c.Clientset.CoreV1().Secrets(newRegistrySecret.Namespace).Update(ctx, newRegistrySecret, metav1.UpdateOptions{}) if err != nil { @@ -170,3 +187,20 @@ func (c *Cluster) UpdateZarfManagedGitSecrets(ctx context.Context, state *types. spinner.Success() } } + +// GetServiceInfoFromRegistryAddress gets the service info for a registry address if it is a NodePort +func (c *Cluster) GetServiceInfoFromRegistryAddress(ctx context.Context, stateRegistryAddress string) (string, error) { + serviceList, err := c.Clientset.CoreV1().Services("").List(ctx, metav1.ListOptions{}) + if err != nil { + return "", err + } + + // If this is an internal service then we need to look it up and + svc, port, err := serviceInfoFromNodePortURL(serviceList.Items, stateRegistryAddress) + if err != nil { + message.Debugf("registry appears to not be a nodeport service, using original address %q", stateRegistryAddress) + return stateRegistryAddress, nil + } + + return fmt.Sprintf("%s:%d", svc.Spec.ClusterIP, port), nil +} diff --git a/src/pkg/cluster/secrets_test.go b/src/pkg/cluster/secrets_test.go index dc9f6c9e22..20d3fe70f9 100644 --- a/src/pkg/cluster/secrets_test.go +++ b/src/pkg/cluster/secrets_test.go @@ -4,25 +4,27 @@ package cluster import ( + "context" "testing" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" "github.com/defenseunicorns/zarf/src/types" ) -func TestGenerateRegistryPullCreds(t *testing.T) { - c := &Cluster{} +func TestGenerateRegistryPullCredsWithOutSvc(t *testing.T) { + c := &Cluster{Clientset: fake.NewSimpleClientset()} + ctx := context.Background() ri := types.RegistryInfo{ - PushUsername: "push-user", - PushPassword: "push-password", PullUsername: "pull-user", PullPassword: "pull-password", Address: "example.com", } - secret := c.GenerateRegistryPullCreds("foo", "bar", ri) + secret, err := c.GenerateRegistryPullCreds(ctx, "foo", "bar", ri) + require.NoError(t, err) expectedSecret := corev1.Secret{ TypeMeta: metav1.TypeMeta{ APIVersion: "v1", @@ -43,11 +45,59 @@ func TestGenerateRegistryPullCreds(t *testing.T) { require.Equal(t, expectedSecret, *secret) } +func TestGenerateRegistryPullCredsWithSvc(t *testing.T) { + c := &Cluster{Clientset: fake.NewSimpleClientset()} + ctx := context.Background() + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "good-service", + Namespace: "whatever", + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeNodePort, + Ports: []corev1.ServicePort{ + { + NodePort: 30001, + Port: 3333, + }, + }, + ClusterIP: "10.11.12.13", + }, + } + + _, err := c.Clientset.CoreV1().Services("whatever").Create(ctx, svc, metav1.CreateOptions{}) + require.NoError(t, err) + + ri := types.RegistryInfo{ + PullUsername: "pull-user", + PullPassword: "pull-password", + Address: "127.0.0.1:30001", + } + secret, err := c.GenerateRegistryPullCreds(ctx, "foo", "bar", ri) + require.NoError(t, err) + expectedSecret := corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "bar", + Namespace: "foo", + Labels: map[string]string{ + ZarfManagedByLabel: "zarf", + }, + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{ + ".dockerconfigjson": []byte(`{"auths":{"10.11.12.13:3333":{"auth":"cHVsbC11c2VyOnB1bGwtcGFzc3dvcmQ="},"127.0.0.1:30001":{"auth":"cHVsbC11c2VyOnB1bGwtcGFzc3dvcmQ="}}}`), + }, + } + require.Equal(t, expectedSecret, *secret) +} + func TestGenerateGitPullCreds(t *testing.T) { c := &Cluster{} gi := types.GitServerInfo{ - PushUsername: "push-user", - PushPassword: "push-password", PullUsername: "pull-user", PullPassword: "pull-password", } diff --git a/src/pkg/cluster/tunnel.go b/src/pkg/cluster/tunnel.go index e09103829d..10a287986d 100644 --- a/src/pkg/cluster/tunnel.go +++ b/src/pkg/cluster/tunnel.go @@ -159,11 +159,11 @@ func (c *Cluster) ConnectToZarfRegistryEndpoint(ctx context.Context, registryInf if err != nil { return "", nil, err } - namespace, name, port, err := serviceInfoFromNodePortURL(serviceList.Items, registryInfo.Address) + svc, port, err := serviceInfoFromNodePortURL(serviceList.Items, registryInfo.Address) // If this is a service (no error getting svcInfo), create a port-forward tunnel to that resource if err == nil { - if tunnel, err = c.NewTunnel(namespace, SvcResource, name, "", 0, port); err != nil { + if tunnel, err = c.NewTunnel(svc.Namespace, SvcResource, svc.Name, "", 0, port); err != nil { return "", tunnel, err } } @@ -255,42 +255,42 @@ func (c *Cluster) findPodContainerPort(ctx context.Context, svc corev1.Service) } // TODO: Refactor to use netip.AddrPort instead of a string for nodePortURL. -func serviceInfoFromNodePortURL(services []corev1.Service, nodePortURL string) (string, string, int, error) { +func serviceInfoFromNodePortURL(services []corev1.Service, nodePortURL string) (corev1.Service, int, error) { // Attempt to parse as normal, if this fails add a scheme to the URL (docker registries don't use schemes) parsedURL, err := url.Parse(nodePortURL) if err != nil { parsedURL, err = url.Parse("scheme://" + nodePortURL) if err != nil { - return "", "", 0, err + return corev1.Service{}, 0, err } } // Match hostname against localhost ip/hostnames hostname := parsedURL.Hostname() if hostname != helpers.IPV4Localhost && hostname != "localhost" { - return "", "", 0, fmt.Errorf("node port services should be on localhost") + return corev1.Service{}, 0, fmt.Errorf("node port services should be on localhost") } // Get the node port from the nodeportURL. nodePort, err := strconv.Atoi(parsedURL.Port()) if err != nil { - return "", "", 0, err + return corev1.Service{}, 0, err } if nodePort < 30000 || nodePort > 32767 { - return "", "", 0, fmt.Errorf("node port services should use the port range 30000-32767") + return corev1.Service{}, 0, fmt.Errorf("node port services should use the port range 30000-32767") } for _, svc := range services { if svc.Spec.Type == "NodePort" { for _, port := range svc.Spec.Ports { if int(port.NodePort) == nodePort { - return svc.Namespace, svc.Name, int(port.Port), nil + return svc, int(port.Port), nil } } } } - return "", "", 0, fmt.Errorf("no matching node port services found") + return corev1.Service{}, 0, fmt.Errorf("no matching node port services found") } // Global lock to synchronize port selections. diff --git a/src/pkg/cluster/tunnel_test.go b/src/pkg/cluster/tunnel_test.go index debe363e54..f9dfc7b5c8 100644 --- a/src/pkg/cluster/tunnel_test.go +++ b/src/pkg/cluster/tunnel_test.go @@ -21,6 +21,7 @@ func TestServiceInfoFromNodePortURL(t *testing.T) { expectedErr string expectedNamespace string expectedName string + expectedIP string expectedPort int }{ { @@ -85,6 +86,7 @@ func TestServiceInfoFromNodePortURL(t *testing.T) { Port: 3333, }, }, + ClusterIP: "good-ip", }, }, { @@ -105,6 +107,7 @@ func TestServiceInfoFromNodePortURL(t *testing.T) { }, expectedNamespace: "good-namespace", expectedName: "good-service", + expectedIP: "good-ip", expectedPort: 3333, }, } @@ -113,15 +116,16 @@ func TestServiceInfoFromNodePortURL(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() - namespace, name, port, err := serviceInfoFromNodePortURL(tt.services, tt.nodePortURL) + svc, port, err := serviceInfoFromNodePortURL(tt.services, tt.nodePortURL) if tt.expectedErr != "" { require.EqualError(t, err, tt.expectedErr) return } require.NoError(t, err) - require.Equal(t, tt.expectedNamespace, namespace) - require.Equal(t, tt.expectedName, name) + require.Equal(t, tt.expectedNamespace, svc.Namespace) + require.Equal(t, tt.expectedName, svc.Name) require.Equal(t, tt.expectedPort, port) + require.Equal(t, tt.expectedIP, svc.Spec.ClusterIP) }) } } diff --git a/src/pkg/transform/image.go b/src/pkg/transform/image.go index 293cb61a64..ca6fcdc820 100644 --- a/src/pkg/transform/image.go +++ b/src/pkg/transform/image.go @@ -38,7 +38,7 @@ func ImageTransformHost(targetHost, srcReference string) (string, error) { // Generate a crc32 hash of the image host + name checksum := helpers.GetCRCHash(image.Name) - // If this image is specified by digest then don't add a checksum it as it will already be a specific SHA + // If this image is specified by digest then don't add a checksum as it will already be a specific SHA if image.Digest != "" { return fmt.Sprintf("%s/%s@%s", targetHost, image.Path, image.Digest), nil } @@ -63,6 +63,8 @@ func ImageTransformHostWithoutChecksum(targetHost, srcReference string) (string, // ParseImageRef parses a source reference into an Image struct func ParseImageRef(srcReference string) (out Image, err error) { + srcReference = strings.TrimPrefix(srcReference, helpers.OCIURLPrefix) + ref, err := reference.ParseAnyReference(srcReference) if err != nil { return out, err diff --git a/src/pkg/transform/image_test.go b/src/pkg/transform/image_test.go index 2edc2e87ec..cc61154efa 100644 --- a/src/pkg/transform/image_test.go +++ b/src/pkg/transform/image_test.go @@ -19,6 +19,7 @@ var imageRefs = []string{ "ghcr.io/stefanprodan/podinfo:6.3.3", "registry1.dso.mil/ironbank/opensource/defenseunicorns/zarf/zarf-agent:v0.25.0", "gitlab.com/project/gitea/gitea:1.19.3-rootless-zarf-3431384023", + "oci://10.43.130.183:5000/stefanprodan/manifests/podinfo", } var badImageRefs = []string{ @@ -38,6 +39,7 @@ func TestImageTransformHost(t *testing.T) { "gitlab.com/project/stefanprodan/podinfo:6.3.3-zarf-2985051089", "gitlab.com/project/ironbank/opensource/defenseunicorns/zarf/zarf-agent:v0.25.0-zarf-2003217571", "gitlab.com/project/gitea/gitea:1.19.3-rootless-zarf-3431384023", + "gitlab.com/project/stefanprodan/manifests/podinfo:latest-zarf-531355090", } for idx, ref := range imageRefs { @@ -62,6 +64,7 @@ func TestImageTransformHostWithoutChecksum(t *testing.T) { "gitlab.com/project/stefanprodan/podinfo:6.3.3", "gitlab.com/project/ironbank/opensource/defenseunicorns/zarf/zarf-agent:v0.25.0", "gitlab.com/project/gitea/gitea:1.19.3-rootless-zarf-3431384023", + "gitlab.com/project/stefanprodan/manifests/podinfo:latest", } for idx, ref := range imageRefs { @@ -86,6 +89,7 @@ func TestParseImageRef(t *testing.T) { {"ghcr.io/", "stefanprodan/podinfo", "6.3.3", ""}, {"registry1.dso.mil/", "ironbank/opensource/defenseunicorns/zarf/zarf-agent", "v0.25.0", ""}, {"gitlab.com/", "project/gitea/gitea", "1.19.3-rootless-zarf-3431384023", ""}, + {"10.43.130.183:5000/", "stefanprodan/manifests/podinfo", "latest", ""}, } for idx, ref := range imageRefs { diff --git a/src/test/e2e/22_git_and_gitops_test.go b/src/test/e2e/22_git_and_gitops_test.go index 7dc712e71a..263ddccb55 100644 --- a/src/test/e2e/22_git_and_gitops_test.go +++ b/src/test/e2e/22_git_and_gitops_test.go @@ -70,15 +70,15 @@ func testGitServerConnect(t *testing.T, gitURL string) { func testGitServerReadOnly(ctx context.Context, t *testing.T, gitURL string) { // Init the state variable - state, err := common.NewClusterOrDie(ctx).LoadZarfState(ctx) + zarfState, err := common.NewClusterOrDie(ctx).LoadZarfState(ctx) require.NoError(t, err) - gitCfg := git.New(state.GitServer) + gitCfg := git.New(zarfState.GitServer) // Get the repo as the readonly user repoName := "zarf-public-test-2469062884" - getRepoRequest, _ := http.NewRequest("GET", fmt.Sprintf("%s/api/v1/repos/%s/%s", gitURL, state.GitServer.PushUsername, repoName), nil) - getRepoResponseBody, _, err := gitCfg.DoHTTPThings(getRepoRequest, types.ZarfGitReadUser, state.GitServer.PullPassword) + getRepoRequest, _ := http.NewRequest("GET", fmt.Sprintf("%s/api/v1/repos/%s/%s", gitURL, zarfState.GitServer.PushUsername, repoName), nil) + getRepoResponseBody, _, err := gitCfg.DoHTTPThings(getRepoRequest, types.ZarfGitReadUser, zarfState.GitServer.PullPassword) require.NoError(t, err) // Make sure the only permissions are pull (read) @@ -93,16 +93,16 @@ func testGitServerReadOnly(ctx context.Context, t *testing.T, gitURL string) { func testGitServerTagAndHash(ctx context.Context, t *testing.T, gitURL string) { // Init the state variable - state, err := common.NewClusterOrDie(ctx).LoadZarfState(ctx) + zarfState, err := common.NewClusterOrDie(ctx).LoadZarfState(ctx) require.NoError(t, err, "Failed to load Zarf state") repoName := "zarf-public-test-2469062884" - gitCfg := git.New(state.GitServer) + gitCfg := git.New(zarfState.GitServer) // Get the Zarf repo tag repoTag := "v0.0.1" getRepoTagsRequest, _ := http.NewRequest("GET", fmt.Sprintf("%s/api/v1/repos/%s/%s/tags/%s", gitURL, types.ZarfGitPushUser, repoName, repoTag), nil) - getRepoTagsResponseBody, _, err := gitCfg.DoHTTPThings(getRepoTagsRequest, types.ZarfGitReadUser, state.GitServer.PullPassword) + getRepoTagsResponseBody, _, err := gitCfg.DoHTTPThings(getRepoTagsRequest, types.ZarfGitReadUser, zarfState.GitServer.PullPassword) require.NoError(t, err) // Make sure the pushed tag exists @@ -114,12 +114,18 @@ func testGitServerTagAndHash(ctx context.Context, t *testing.T, gitURL string) { // Get the Zarf repo commit repoHash := "01a23218923f24194133b5eb11268cf8d73ff1bb" getRepoCommitsRequest, _ := http.NewRequest("GET", fmt.Sprintf("%s/api/v1/repos/%s/%s/git/commits/%s", gitURL, types.ZarfGitPushUser, repoName, repoHash), nil) - getRepoCommitsResponseBody, _, err := gitCfg.DoHTTPThings(getRepoCommitsRequest, types.ZarfGitReadUser, state.GitServer.PullPassword) + getRepoCommitsResponseBody, _, err := gitCfg.DoHTTPThings(getRepoCommitsRequest, types.ZarfGitReadUser, zarfState.GitServer.PullPassword) require.NoError(t, err) require.Contains(t, string(getRepoCommitsResponseBody), repoHash) } func waitFluxPodInfoDeployment(t *testing.T) { + ctx := context.Background() + cluster := common.NewClusterOrDie(ctx) + zarfState, err := cluster.LoadZarfState(ctx) + require.NoError(t, err, "Failed to load Zarf state") + registryAddress, err := cluster.GetServiceInfoFromRegistryAddress(ctx, zarfState.RegistryInfo.Address) + require.NoError(t, err) // Deploy the flux example and verify that it works path := fmt.Sprintf("build/zarf-package-podinfo-flux-%s.tar.zst", e2e.Arch) stdOut, stdErr, err := e2e.Zarf("package", "deploy", path, "--confirm") @@ -131,6 +137,26 @@ func waitFluxPodInfoDeployment(t *testing.T) { expectedMutatedRepoURL := fmt.Sprintf("%s/%s/podinfo-1646971829.git", types.ZarfInClusterGitServiceURL, types.ZarfGitPushUser) require.Equal(t, expectedMutatedRepoURL, stdOut) + // Tests the URL mutation for HelmRepository CRD for Flux. + stdOut, stdErr, err = e2e.Kubectl("get", "helmrepositories", "podinfo", "-n", "flux-system", "-o", "jsonpath={.spec.url}") + require.NoError(t, err, stdOut, stdErr) + expectedMutatedRepoURL = fmt.Sprintf("oci://%s/stefanprodan/charts", registryAddress) + require.Equal(t, expectedMutatedRepoURL, stdOut) + stdOut, stdErr, err = e2e.Kubectl("get", "helmrelease", "podinfo", "-n", "flux-system", "-o", "jsonpath={.spec.chart.spec.version}") + require.NoError(t, err, stdOut, stdErr) + expectedMutatedRepoTag := "6.4.0" + require.Equal(t, expectedMutatedRepoTag, stdOut) + + // Tests the URL mutation for OCIRepository CRD for Flux. + stdOut, stdErr, err = e2e.Kubectl("get", "ocirepositories", "podinfo", "-n", "flux-system", "-o", "jsonpath={.spec.url}") + require.NoError(t, err, stdOut, stdErr) + expectedMutatedRepoURL = fmt.Sprintf("oci://%s/stefanprodan/manifests/podinfo", registryAddress) + require.Equal(t, expectedMutatedRepoURL, stdOut) + stdOut, stdErr, err = e2e.Kubectl("get", "ocirepositories", "podinfo", "-n", "flux-system", "-o", "jsonpath={.spec.ref.tag}") + require.NoError(t, err, stdOut, stdErr) + expectedMutatedRepoTag = "6.4.0-zarf-2823281104" + require.Equal(t, expectedMutatedRepoTag, stdOut) + // Remove the flux example when deployment completes stdOut, stdErr, err = e2e.Zarf("package", "remove", "podinfo-flux", "--confirm") require.NoError(t, err, stdOut, stdErr) diff --git a/src/test/external/common.go b/src/test/external/common.go index 5ce0efc68b..2e9cd8bb68 100644 --- a/src/test/external/common.go +++ b/src/test/external/common.go @@ -7,18 +7,29 @@ package external import ( "context" "path" + "path/filepath" "strings" "testing" "time" "github.com/defenseunicorns/zarf/src/pkg/utils/exec" "github.com/defenseunicorns/zarf/src/test" + "github.com/otiai10/copy" + "github.com/stretchr/testify/require" ) var zarfBinPath = path.Join("../../../build", test.GetCLIName()) -func verifyKubectlWaitSuccess(t *testing.T, timeoutMinutes time.Duration, args []string, onTimeout string) bool { - return verifyWaitSuccess(t, timeoutMinutes, "kubectl", args, "condition met", onTimeout) +func createPodInfoPackageWithInsecureSources(t *testing.T, temp string) { + err := copy.Copy("../../../examples/podinfo-flux", temp) + require.NoError(t, err) + // This is done because while .spec.insecure is auto set to true for internal registries by the agent + // it is not for external registries, however since we are using an insecure external registry, we still need it + err = exec.CmdWithPrint(zarfBinPath, "tools", "yq", "eval", ".spec.insecure = true", "-i", filepath.Join(temp, "helm", "podinfo-source.yaml")) + require.NoError(t, err, "unable to yq edit helm source") + err = exec.CmdWithPrint(zarfBinPath, "tools", "yq", "eval", ".spec.insecure = true", "-i", filepath.Join(temp, "oci", "podinfo-source.yaml")) + require.NoError(t, err, "unable to yq edit oci source") + exec.CmdWithPrint(zarfBinPath, "package", "create", temp, "--confirm", "--output", temp) } func verifyWaitSuccess(t *testing.T, timeoutMinutes time.Duration, cmd string, args []string, condition string, onTimeout string) bool { diff --git a/src/test/external/ext_in_cluster_test.go b/src/test/external/ext_in_cluster_test.go index 3338b5474f..655cb7c59a 100644 --- a/src/test/external/ext_in_cluster_test.go +++ b/src/test/external/ext_in_cluster_test.go @@ -9,12 +9,18 @@ import ( "fmt" "io" "net/http" + "os" + "path/filepath" "testing" + "time" + pkgkubernetes "github.com/defenseunicorns/pkg/kubernetes" "github.com/defenseunicorns/zarf/src/pkg/cluster" "github.com/defenseunicorns/zarf/src/pkg/utils/exec" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/cli-utils/pkg/object" ) var inClusterCredentialArgs = []string{ @@ -31,6 +37,7 @@ type ExtInClusterTestSuite struct { } func (suite *ExtInClusterTestSuite) SetupSuite() { + fmt.Println("start: current time is ", time.Now()) suite.Assertions = require.New(suite.T()) // Install a gitea chart to the k8s cluster to act as the 'remote' git server @@ -49,14 +56,29 @@ func (suite *ExtInClusterTestSuite) SetupSuite() { suite.NoError(err, "unable to install the docker-registry chart") // Verify the registry and gitea helm charts installed successfully - registryWaitCmd := []string{"wait", "deployment", "-n=external-registry", "external-registry-docker-registry", "--for", "condition=Available=True", "--timeout=5s"} - registryErrStr := "unable to verify the docker-registry chart installed successfully" - giteaWaitCmd := []string{"wait", "pod", "-n=git-server", "gitea-0", "--for", "condition=Ready=True", "--timeout=5s"} - giteaErrStr := "unable to verify the gitea chart installed successfully" - success := verifyKubectlWaitSuccess(suite.T(), 2, registryWaitCmd, registryErrStr) - suite.True(success, registryErrStr) - success = verifyKubectlWaitSuccess(suite.T(), 3, giteaWaitCmd, giteaErrStr) - suite.True(success, giteaErrStr) + c, err := cluster.NewCluster() + suite.NoError(err) + objs := []object.ObjMetadata{ + { + GroupKind: schema.GroupKind{ + Group: "apps", + Kind: "Deployment", + }, + Namespace: "external-registry", + Name: "external-registry-docker-registry", + }, + { + GroupKind: schema.GroupKind{ + Kind: "Pod", + }, + Namespace: "git-server", + Name: "gitea-0", + }, + } + waitCtx, waitCancel := context.WithTimeout(context.Background(), 60*time.Second) + defer waitCancel() + err = pkgkubernetes.WaitForReady(waitCtx, c.Watcher, objs) + suite.NoError(err) } func (suite *ExtInClusterTestSuite) TearDownSuite() { @@ -123,17 +145,47 @@ func (suite *ExtInClusterTestSuite) Test_1_Deploy() { initArgs = append(initArgs, inClusterCredentialArgs...) err := exec.CmdWithPrint(zarfBinPath, initArgs...) suite.NoError(err, "unable to initialize the k8s server with zarf") + temp := suite.T().TempDir() + defer os.Remove(temp) + createPodInfoPackageWithInsecureSources(suite.T(), temp) // Deploy the flux example package - deployArgs := []string{"package", "deploy", "../../../build/zarf-package-podinfo-flux-amd64.tar.zst", "--confirm"} + deployArgs := []string{"package", "deploy", filepath.Join(temp, "zarf-package-podinfo-flux-amd64.tar.zst"), "--confirm"} err = exec.CmdWithPrint(zarfBinPath, deployArgs...) suite.NoError(err, "unable to deploy flux example package") - // Verify flux was able to pull from the 'external' repository - podinfoWaitCmd := []string{"wait", "deployment", "-n=podinfo", "podinfo", "--for", "condition=Available=True", "--timeout=3s"} - errorStr := "unable to verify flux deployed the podinfo example" - success := verifyKubectlWaitSuccess(suite.T(), 2, podinfoWaitCmd, errorStr) - suite.True(success, errorStr) + c, err := cluster.NewCluster() + suite.NoError(err) + objs := []object.ObjMetadata{ + { + GroupKind: schema.GroupKind{ + Group: "apps", + Kind: "Deployment", + }, + Namespace: "podinfo-git", + Name: "podinfo", + }, + { + GroupKind: schema.GroupKind{ + Group: "apps", + Kind: "Deployment", + }, + Namespace: "podinfo-helm", + Name: "podinfo", + }, + { + GroupKind: schema.GroupKind{ + Group: "apps", + Kind: "Deployment", + }, + Namespace: "podinfo-oci", + Name: "podinfo", + }, + } + waitCtx, waitCancel := context.WithTimeout(context.Background(), 60*time.Second) + defer waitCancel() + err = pkgkubernetes.WaitForReady(waitCtx, c.Watcher, objs) + suite.NoError(err) _, _, err = exec.CmdWithContext(context.TODO(), exec.PrintCfg(), zarfBinPath, "destroy", "--confirm") suite.NoError(err, "unable to teardown zarf") diff --git a/src/test/external/ext_out_cluster_test.go b/src/test/external/ext_out_cluster_test.go index 050150e5fa..b32960c318 100644 --- a/src/test/external/ext_out_cluster_test.go +++ b/src/test/external/ext_out_cluster_test.go @@ -29,6 +29,7 @@ const ( subnet = "172.31.0.0/16" gateway = "172.31.0.1" giteaIP = "172.31.0.99" + registryIP = "172.31.0.10" giteaHost = "gitea.localhost" registryHost = "registry.localhost" clusterName = "zarf-external-test" @@ -43,7 +44,9 @@ var outClusterCredentialArgs = []string{ "--git-url=http://" + giteaHost + ":3000", "--registry-push-username=" + registryUser, "--registry-push-password=" + commonPassword, - "--registry-url=k3d-" + registryHost + ":5000"} + // TODO @AustinAbro321 once flux updates to a version of helm using ORAS v1.2.5 or greater we can switch back + // to using the registry host rather than creating an IP https://github.com/helm/helm/pull/12998 + "--registry-url=" + registryIP + ":5000"} type ExtOutClusterTestSuite struct { suite.Suite @@ -56,7 +59,8 @@ func (suite *ExtOutClusterTestSuite) SetupSuite() { // Teardown any leftovers from previous tests _ = exec.CmdWithPrint("k3d", "cluster", "delete", clusterName) - _ = exec.CmdWithPrint("k3d", "registry", "delete", registryHost) + _ = exec.CmdWithPrint("docker", "rm", "-f", "k3d-"+registryHost) + _ = exec.CmdWithPrint("docker", "compose", "down") _ = exec.CmdWithPrint("docker", "network", "remove", network) // Setup a network for everything to live inside @@ -64,11 +68,12 @@ func (suite *ExtOutClusterTestSuite) SetupSuite() { suite.NoError(err, "unable to create the k3d registry") // Install a k3d-managed registry server to act as the 'remote' container registry - err = exec.CmdWithPrint("k3d", "registry", "create", registryHost, "--port", "5000") + err = exec.CmdWithPrint("docker", "run", "-d", "--restart=always", "-p", "5000:5000", "--name", "k3d-"+registryHost, "registry:2.8.3") suite.NoError(err, "unable to create the k3d registry") // Create a k3d cluster with the proper networking and aliases - err = exec.CmdWithPrint("k3d", "cluster", "create", clusterName, "--registry-use", "k3d-"+registryHost+":5000", "--host-alias", giteaIP+":"+giteaHost, "--network", network) + err = exec.CmdWithPrint("k3d", "cluster", "create", clusterName, "--registry-config", "registries.yaml", + "--host-alias", registryIP+":"+registryHost, "--host-alias", giteaIP+":"+giteaHost, "--network", network) suite.NoError(err, "unable to create the k3d cluster") // Install a gitea server via docker compose to act as the 'remote' git server @@ -83,7 +88,9 @@ func (suite *ExtOutClusterTestSuite) SetupSuite() { // Connect gitea to the k3d network err = exec.CmdWithPrint("docker", "network", "connect", "--ip", giteaIP, network, giteaHost) - suite.NoError(err, "unable to connect the gitea-server top k3d") + suite.NoError(err, "unable to connect the gitea-server to k3d") + err = exec.CmdWithPrint("docker", "network", "connect", "--ip", registryIP, network, "k3d-"+registryHost) + suite.NoError(err, "unable to connect the registry-server to k3d") } func (suite *ExtOutClusterTestSuite) TearDownSuite() { @@ -94,7 +101,7 @@ func (suite *ExtOutClusterTestSuite) TearDownSuite() { err = exec.CmdWithPrint("docker", "compose", "down") suite.NoError(err, "unable to teardown the gitea-server") - err = exec.CmdWithPrint("k3d", "registry", "delete", registryHost) + err = exec.CmdWithPrint("docker", "rm", "-f", "k3d-"+registryHost) suite.NoError(err, "unable to teardown the k3d registry") err = exec.CmdWithPrint("docker", "network", "remove", network) @@ -135,20 +142,25 @@ func (suite *ExtOutClusterTestSuite) Test_1_Deploy() { initArgs = append(initArgs, outClusterCredentialArgs...) err := exec.CmdWithPrint(zarfBinPath, initArgs...) suite.NoError(err, "unable to initialize the k8s server with zarf") +} +func (suite *ExtOutClusterTestSuite) Test_2_DeployGitOps() { // Deploy the flux example package - deployArgs := []string{"package", "deploy", "../../../build/zarf-package-podinfo-flux-amd64.tar.zst", "--confirm"} - err = exec.CmdWithPrint(zarfBinPath, deployArgs...) + temp := suite.T().TempDir() + defer os.Remove(temp) + createPodInfoPackageWithInsecureSources(suite.T(), temp) + + deployArgs := []string{"package", "deploy", filepath.Join(temp, "zarf-package-podinfo-flux-amd64.tar.zst"), "--confirm"} + err := exec.CmdWithPrint(zarfBinPath, deployArgs...) suite.NoError(err, "unable to deploy flux example package") - // Verify flux was able to pull from the 'external' repository - podinfoArgs := []string{"wait", "deployment", "-n=podinfo", "podinfo", "--for", "condition=Available=True", "--timeout=3s"} - errorStr := "unable to verify flux deployed the podinfo example" - success := verifyKubectlWaitSuccess(suite.T(), 2, podinfoArgs, errorStr) - suite.True(success, errorStr) + path := fmt.Sprintf("../../../build/zarf-package-argocd-%s.tar.zst", "amd64") + deployArgs = []string{"package", "deploy", path, "--confirm"} + err = exec.CmdWithPrint(zarfBinPath, deployArgs...) + suite.NoError(err) } -func (suite *ExtOutClusterTestSuite) Test_2_AuthToPrivateHelmChart() { +func (suite *ExtOutClusterTestSuite) Test_3_AuthToPrivateHelmChart() { baseURL := fmt.Sprintf("http://%s:3000", giteaHost) suite.createHelmChartInGitea(baseURL, giteaUser, commonPassword) diff --git a/src/test/external/registries.yaml b/src/test/external/registries.yaml new file mode 100644 index 0000000000..8e7ac32100 --- /dev/null +++ b/src/test/external/registries.yaml @@ -0,0 +1,4 @@ +mirrors: + "172.31.0.10:5000": + endpoint: + - http://172.31.0.10:5000