Open bob-rohan opened 2 years ago
You can place these markers on the Flux Kustomization object that applies the app on an environment. The one in clusters/staging
will use different policy than the one in clusters/production
e.g.:
clusters/staging/podinfo.yaml
:
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: podinfo
namespace: default
spec:
images:
- name: ghcr.io/stefanprodan/podinfo
newName: ghcr.io/stefanprodan/podinfo # {"$imagepolicy": "flux-system:podinfo-release:name"}
newTag: 5.0.0 # {"$imagepolicy": "flux-system:podinfo-release:tag"}
clusters/production/podinfo.yaml
:
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: podinfo
namespace: default
spec:
images:
- name: ghcr.io/stefanprodan/podinfo
newName: ghcr.io/stefanprodan/podinfo # {"$imagepolicy": "flux-system:podinfo-main:name"}
newTag: 5.0.0 # {"$imagepolicy": "flux-system:podinfo-main:tag"}
I believe in your example, you may have reduced the effectiveness of Flux by introducing additional overhead, or risk, or both - depending on what your strategy is for promotion.
In the example quoted there are a few material differences
5.0.0
don't differentiate between production and staging, which suggest there is no difference in the podinfo-release
and podinfo-main
policies. See my example for the <branch>-<commit sha>-<timestamp>
formatrelease
, the production environment deploys main
. It's the other way around in your example, which may be anecdotal but would be useful to avoid further confusionBased on this feedback, if you could elaborate on the mechanism/process you see for promoting between environments, I can provide further clarity.
Ok so there are no overlays for each env, the staging cluster is identical to the production one, nothing differs between the 2 branches but the image tags?
I think the short answer is nothing relevant, but I'll include some detail incase you're driving at a point I don't yet understand.
In the fleet repo, both staging and production clusters declare a several GitRepository
kind's.
Flux Kustomization
yaml enacts reconcile loop on image-ids. Both manifests and application-configuration are embedded via include
, with kustomization.yaml
referring to relative paths.
The GitRepository
refspec if different per environment
staging -> image-ids release
production -> image-ids main
Image update automation is setup different per environment (setup in the fleet repo)
staging -> '-release' image policy searching ECR for docker tag matching release-<sha>-<timestamp>
, updating image-ids release
branch via push
production -> '-main' image policy searching ECR for docker tag matching main-<sha>-<timestamp>
, updating image-ids main
branch via PR
Few additional things are fed through from the fleet repo to the manifests via Flux Kustomization
overlay, things like the environment name for nonprod
would be staging
, as apposed to the uat
environment also within the nonprod cluster. But your terminology of staging
cluster is entirely sufficient to frame the issue.
In terms of image-ids repo, multiple branches
Both will have the same structure, i.e a kustomization.yaml
file declaring a collection of the tenant's images. Both the image tags and the inline image policy marker comments will differ between branches.
Ok so you could use a single policy and swap the filter based on the target cluster, that marker will be the same (no merge issues) but they will act differently depending on the cluster.
Yes, I think I can see how that would work for the example above.
We have multiple policies in some clusters, but by refactoring the image automation CRDs from flux-system namespace to the tenant namespace, I think that could work also.
I will have a go. Thanks @stefanprodan
Been a bit of a day going back and forth on this one. Main fumble point seems to be around what namespace various components logically reside within, Secret (for git), GitRepository, ecr-credentails-sync, ImagePolicy, ImageRegistry, ImageAutomation etc
I think I'm just about where I want to be on this one but for one snag. I feel lucky this merges some cross namespace functionality I'd like to use, for better or worse
While Flux cli 0.26.2 (latest) includes image-automation-controller > 0.20.0 (see link above), it would seem the ImageUpdateAutomation schema defined by flux boostrap does not set namespace as a valid arg.
If the above is correct, how long to get that functionality available please?
While Flux cli 0.26.2 (latest) includes image-automation-controller > 0.20.0 (see link above), it would seem the ImageUpdateAutomation schema defined by flux boostrap does not set namespace as a valid arg.
Have you upgraded your cluster? Can you post here kubectl get crd imageupdateautomations.image.toolkit.fluxcd.io -oyaml
?
Yes, well, sort of. We dump the flux bootstrap results out to disk and store in git for bootstrapping via terraform initiated custom ECS task. Gives us a sensible way to PR and control rollout.
Anyway result for your command suggests I upgraded to 0.24
, but if you at the results from flux boostrap with cli 0.26 - it's my interpretation that the ImageUpdateAutomation shows
namespace` is not a valid attribute under that schema for sourceref. I will post another comment with that output as size is large
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.5.0
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"apiextensions.k8s.io/v1","kind":"CustomResourceDefinition","metadata":{"annotations":{"controller-gen.kubebuilder.io/version":"v0.5.0"},"creationTimestamp":null,"labels":{"app.kubernetes.io/instance":"flux-system","app.kubernetes.io/part-of":"flux","app.kubernetes.io/version":"v0.24.1"},"name":"imageupdateautomations.image.toolkit.fluxcd.io"},"spec":{"group":"image.toolkit.fluxcd.io","names":{"kind":"ImageUpdateAutomation","listKind":"ImageUpdateAutomationList","plural":"imageupdateautomations","singular":"imageupdateautomation"},"scope":"Namespaced","versions":[{"additionalPrinterColumns":[{"jsonPath":".status.lastAutomationRunTime","name":"Last run","type":"string"}],"name":"v1alpha1","schema":{"openAPIV3Schema":{"description":"ImageUpdateAutomation is the Schema for the imageupdateautomations 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":"ImageUpdateAutomationSpec defines the desired state of ImageUpdateAutomation","properties":{"checkout":{"description":"Checkout gives the parameters for cloning the git repository, ready to make changes.","properties":{"branch":{"description":"Branch gives the branch to clone from the git repository. If `.spec.push` is not supplied, commits will also be pushed to this branch.","type":"string"},"gitRepositoryRef":{"description":"GitRepositoryRef refers to the resource giving access details to a git repository to update files in.","properties":{"name":{"description":"Name of the referent","type":"string"}},"required":["name"],"type":"object"}},"required":["branch","gitRepositoryRef"],"type":"object"},"commit":{"description":"Commit specifies how to commit to the git repository.","properties":{"authorEmail":{"description":"AuthorEmail gives the email to provide when making a commit","type":"string"},"authorName":{"description":"AuthorName gives the name to provide when making a commit","type":"string"},"messageTemplate":{"description":"MessageTemplate provides a template for the commit message, into which will be interpolated the details of the change made.","type":"string"},"signingKey":{"description":"SigningKey provides the option to sign commits with a GPG key","properties":{"secretRef":{"description":"SecretRef holds the name to a secret that contains a 'git.asc' key corresponding to the ASCII Armored file containing the GPG signing keypair as the value. It must be in the same namespace as the ImageUpdateAutomation.","properties":{"name":{"description":"Name of the referent","type":"string"}},"required":["name"],"type":"object"}},"type":"object"}},"required":["authorEmail","authorName"],"type":"object"},"interval":{"description":"Interval gives an lower bound for how often the automation run should be attempted.","type":"string"},"push":{"description":"Push specifies how and where to push commits made by the automation. If missing, commits are pushed (back) to `.spec.checkout.branch`.","properties":{"branch":{"description":"Branch specifies that commits should be pushed to the branch named. The branch is created using `.spec.checkout.branch` as the starting point, if it doesn't already exist.","type":"string"}},"required":["branch"],"type":"object"},"suspend":{"description":"Suspend tells the controller to not run this automation, until it is unset (or set to false). Defaults to false.","type":"boolean"},"update":{"default":{"strategy":"Setters"},"description":"Update gives the specification for how to update the files in the repository. This can be left empty, to use the default value.","properties":{"path":{"description":"Path to the directory containing the manifests to be updated. Defaults to 'None', which translates to the root path of the GitRepositoryRef.","type":"string"},"strategy":{"default":"Setters","description":"Strategy names the strategy to be used.","enum":["Setters"],"type":"string"}},"required":["strategy"],"type":"object"}},"required":["checkout","commit","interval"],"type":"object"},"status":{"description":"ImageUpdateAutomationStatus defines the observed state of ImageUpdateAutomation","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, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }","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"},"lastAutomationRunTime":{"description":"LastAutomationRunTime records the last time the controller ran this automation through to completion (even if no updates were made).","format":"date-time","type":"string"},"lastHandledReconcileAt":{"description":"LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change can be detected.","type":"string"},"lastPushCommit":{"description":"LastPushCommit records the SHA1 of the last commit made by the controller, for this automation object","type":"string"},"lastPushTime":{"description":"LastPushTime records the time of the last pushed change.","format":"date-time","type":"string"},"observedGeneration":{"format":"int64","type":"integer"}},"type":"object"}},"type":"object"}},"served":true,"storage":false,"subresources":{"status":{}}},{"additionalPrinterColumns":[{"jsonPath":".status.lastAutomationRunTime","name":"Last run","type":"string"}],"name":"v1alpha2","schema":{"openAPIV3Schema":{"description":"ImageUpdateAutomation is the Schema for the imageupdateautomations 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":"ImageUpdateAutomationSpec defines the desired state of ImageUpdateAutomation","properties":{"git":{"description":"GitSpec contains all the git-specific definitions. This is technically optional, but in practice mandatory until there are other kinds of source allowed.","properties":{"checkout":{"description":"Checkout gives the parameters for cloning the git repository, ready to make changes. If not present, the `spec.ref` field from the referenced `GitRepository` or its default will be used.","properties":{"ref":{"description":"Reference gives a branch, tag or commit to clone from the Git repository.","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"}},"required":["ref"],"type":"object"},"commit":{"description":"Commit specifies how to commit to the git repository.","properties":{"author":{"description":"Author gives the email and optionally the name to use as the author of commits.","properties":{"email":{"description":"Email gives the email to provide when making a commit.","type":"string"},"name":{"description":"Name gives the name to provide when making a commit.","type":"string"}},"required":["email"],"type":"object"},"messageTemplate":{"description":"MessageTemplate provides a template for the commit message, into which will be interpolated the details of the change made.","type":"string"},"signingKey":{"description":"SigningKey provides the option to sign commits with a GPG key","properties":{"secretRef":{"description":"SecretRef holds the name to a secret that contains a 'git.asc' key corresponding to the ASCII Armored file containing the GPG signing keypair as the value. It must be in the same namespace as the ImageUpdateAutomation.","properties":{"name":{"description":"Name of the referent","type":"string"}},"required":["name"],"type":"object"}},"type":"object"}},"required":["author"],"type":"object"},"push":{"description":"Push specifies how and where to push commits made by the automation. If missing, commits are pushed (back) to `.spec.checkout.branch` or its default.","properties":{"branch":{"description":"Branch specifies that commits should be pushed to the branch named. The branch is created using `.spec.checkout.branch` as the starting point, if it doesn't already exist.","type":"string"}},"required":["branch"],"type":"object"}},"required":["commit"],"type":"object"},"interval":{"description":"Interval gives an lower bound for how often the automation run should be attempted.","type":"string"},"sourceRef":{"description":"SourceRef refers to the resource giving access details to a git repository.","properties":{"apiVersion":{"description":"API version of the referent","type":"string"},"kind":{"default":"GitRepository","description":"Kind of the referent","enum":["GitRepository"],"type":"string"},"name":{"description":"Name of the referent","type":"string"}},"required":["kind","name"],"type":"object"},"suspend":{"description":"Suspend tells the controller to not run this automation, until it is unset (or set to false). Defaults to false.","type":"boolean"},"update":{"default":{"strategy":"Setters"},"description":"Update gives the specification for how to update the files in the repository. This can be left empty, to use the default value.","properties":{"path":{"description":"Path to the directory containing the manifests to be updated. Defaults to 'None', which translates to the root path of the GitRepositoryRef.","type":"string"},"strategy":{"default":"Setters","description":"Strategy names the strategy to be used.","enum":["Setters"],"type":"string"}},"required":["strategy"],"type":"object"}},"required":["interval","sourceRef"],"type":"object"},"status":{"description":"ImageUpdateAutomationStatus defines the observed state of ImageUpdateAutomation","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, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }","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"},"lastAutomationRunTime":{"description":"LastAutomationRunTime records the last time the controller ran this automation through to completion (even if no updates were made).","format":"date-time","type":"string"},"lastHandledReconcileAt":{"description":"LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change can be detected.","type":"string"},"lastPushCommit":{"description":"LastPushCommit records the SHA1 of the last commit made by the controller, for this automation object","type":"string"},"lastPushTime":{"description":"LastPushTime records the time of the last pushed change.","format":"date-time","type":"string"},"observedGeneration":{"format":"int64","type":"integer"}},"type":"object"}},"type":"object"}},"served":true,"storage":false,"subresources":{"status":{}}},{"additionalPrinterColumns":[{"jsonPath":".status.lastAutomationRunTime","name":"Last run","type":"string"}],"name":"v1beta1","schema":{"openAPIV3Schema":{"description":"ImageUpdateAutomation is the Schema for the imageupdateautomations 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":"ImageUpdateAutomationSpec defines the desired state of ImageUpdateAutomation","properties":{"git":{"description":"GitSpec contains all the git-specific definitions. This is technically optional, but in practice mandatory until there are other kinds of source allowed.","properties":{"checkout":{"description":"Checkout gives the parameters for cloning the git repository, ready to make changes. If not present, the `spec.ref` field from the referenced `GitRepository` or its default will be used.","properties":{"ref":{"description":"Reference gives a branch, tag or commit to clone from the Git repository.","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"}},"required":["ref"],"type":"object"},"commit":{"description":"Commit specifies how to commit to the git repository.","properties":{"author":{"description":"Author gives the email and optionally the name to use as the author of commits.","properties":{"email":{"description":"Email gives the email to provide when making a commit.","type":"string"},"name":{"description":"Name gives the name to provide when making a commit.","type":"string"}},"required":["email"],"type":"object"},"messageTemplate":{"description":"MessageTemplate provides a template for the commit message, into which will be interpolated the details of the change made.","type":"string"},"signingKey":{"description":"SigningKey provides the option to sign commits with a GPG key","properties":{"secretRef":{"description":"SecretRef holds the name to a secret that contains a 'git.asc' key corresponding to the ASCII Armored file containing the GPG signing keypair as the value. It must be in the same namespace as the ImageUpdateAutomation.","properties":{"name":{"description":"Name of the referent","type":"string"}},"required":["name"],"type":"object"}},"type":"object"}},"required":["author"],"type":"object"},"push":{"description":"Push specifies how and where to push commits made by the automation. If missing, commits are pushed (back) to `.spec.checkout.branch` or its default.","properties":{"branch":{"description":"Branch specifies that commits should be pushed to the branch named. The branch is created using `.spec.checkout.branch` as the starting point, if it doesn't already exist.","type":"string"}},"required":["branch"],"type":"object"}},"required":["commit"],"type":"object"},"interval":{"description":"Interval gives an lower bound for how often the automation run should be attempted.","type":"string"},"sourceRef":{"description":"SourceRef refers to the resource giving access details to a git repository.","properties":{"apiVersion":{"description":"API version of the referent","type":"string"},"kind":{"default":"GitRepository","description":"Kind of the referent","enum":["GitRepository"],"type":"string"},"name":{"description":"Name of the referent","type":"string"}},"required":["kind","name"],"type":"object"},"suspend":{"description":"Suspend tells the controller to not run this automation, until it is unset (or set to false). Defaults to false.","type":"boolean"},"update":{"default":{"strategy":"Setters"},"description":"Update gives the specification for how to update the files in the repository. This can be left empty, to use the default value.","properties":{"path":{"description":"Path to the directory containing the manifests to be updated. Defaults to 'None', which translates to the root path of the GitRepositoryRef.","type":"string"},"strategy":{"default":"Setters","description":"Strategy names the strategy to be used.","enum":["Setters"],"type":"string"}},"required":["strategy"],"type":"object"}},"required":["interval","sourceRef"],"type":"object"},"status":{"description":"ImageUpdateAutomationStatus defines the observed state of ImageUpdateAutomation","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, type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }","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"},"lastAutomationRunTime":{"description":"LastAutomationRunTime records the last time the controller ran this automation through to completion (even if no updates were made).","format":"date-time","type":"string"},"lastHandledReconcileAt":{"description":"LastHandledReconcileAt holds the value of the most recent reconcile request value, so a change can be detected.","type":"string"},"lastPushCommit":{"description":"LastPushCommit records the SHA1 of the last commit made by the controller, for this automation object","type":"string"},"lastPushTime":{"description":"LastPushTime records the time of the last pushed change.","format":"date-time","type":"string"},"observedGeneration":{"format":"int64","type":"integer"}},"type":"object"}},"type":"object"}},"served":true,"storage":true,"subresources":{"status":{}}}]},"status":{"acceptedNames":{"kind":"","plural":""},"conditions":[],"storedVersions":[]}}
creationTimestamp: "2022-02-09T20:58:20Z"
generation: 1
labels:
app.kubernetes.io/instance: flux-system
app.kubernetes.io/part-of: flux
app.kubernetes.io/version: v0.24.1
name: imageupdateautomations.image.toolkit.fluxcd.io
resourceVersion: "881"
uid: 354da05f-9606-4fd7-8302-d3838f9a8b03
spec:
conversion:
strategy: None
group: image.toolkit.fluxcd.io
names:
kind: ImageUpdateAutomation
listKind: ImageUpdateAutomationList
plural: imageupdateautomations
singular: imageupdateautomation
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .status.lastAutomationRunTime
name: Last run
type: string
name: v1alpha1
schema:
openAPIV3Schema:
description: ImageUpdateAutomation is the Schema for the imageupdateautomations
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: ImageUpdateAutomationSpec defines the desired state of ImageUpdateAutomation
properties:
checkout:
description: Checkout gives the parameters for cloning the git repository,
ready to make changes.
properties:
branch:
description: Branch gives the branch to clone from the git repository.
If `.spec.push` is not supplied, commits will also be pushed
to this branch.
type: string
gitRepositoryRef:
description: GitRepositoryRef refers to the resource giving access
details to a git repository to update files in.
properties:
name:
description: Name of the referent
type: string
required:
- name
type: object
required:
- branch
- gitRepositoryRef
type: object
commit:
description: Commit specifies how to commit to the git repository.
properties:
authorEmail:
description: AuthorEmail gives the email to provide when making
a commit
type: string
authorName:
description: AuthorName gives the name to provide when making
a commit
type: string
messageTemplate:
description: MessageTemplate provides a template for the commit
message, into which will be interpolated the details of the
change made.
type: string
signingKey:
description: SigningKey provides the option to sign commits with
a GPG key
properties:
secretRef:
description: SecretRef holds the name to a secret that contains
a 'git.asc' key corresponding to the ASCII Armored file
containing the GPG signing keypair as the value. It must
be in the same namespace as the ImageUpdateAutomation.
properties:
name:
description: Name of the referent
type: string
required:
- name
type: object
type: object
required:
- authorEmail
- authorName
type: object
interval:
description: Interval gives an lower bound for how often the automation
run should be attempted.
type: string
push:
description: Push specifies how and where to push commits made by
the automation. If missing, commits are pushed (back) to `.spec.checkout.branch`.
properties:
branch:
description: Branch specifies that commits should be pushed to
the branch named. The branch is created using `.spec.checkout.branch`
as the starting point, if it doesn't already exist.
type: string
required:
- branch
type: object
suspend:
description: Suspend tells the controller to not run this automation,
until it is unset (or set to false). Defaults to false.
type: boolean
update:
default:
strategy: Setters
description: Update gives the specification for how to update the
files in the repository. This can be left empty, to use the default
value.
properties:
path:
description: Path to the directory containing the manifests to
be updated. Defaults to 'None', which translates to the root
path of the GitRepositoryRef.
type: string
strategy:
default: Setters
description: Strategy names the strategy to be used.
enum:
- Setters
type: string
required:
- strategy
type: object
required:
- checkout
- commit
- interval
type: object
status:
description: ImageUpdateAutomationStatus defines the observed state of
ImageUpdateAutomation
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,
type FooStatus struct{ // Represents the observations of a
foo's current state. // Known .status.conditions.type are:
\"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type
\ // +patchStrategy=merge // +listType=map // +listMapKey=type
\ Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
\n // other fields }"
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
lastAutomationRunTime:
description: LastAutomationRunTime records the last time the controller
ran this automation through to completion (even if no updates were
made).
format: date-time
type: string
lastHandledReconcileAt:
description: LastHandledReconcileAt holds the value of the most recent
reconcile request value, so a change can be detected.
type: string
lastPushCommit:
description: LastPushCommit records the SHA1 of the last commit made
by the controller, for this automation object
type: string
lastPushTime:
description: LastPushTime records the time of the last pushed change.
format: date-time
type: string
observedGeneration:
format: int64
type: integer
type: object
type: object
served: true
storage: false
subresources:
status: {}
- additionalPrinterColumns:
- jsonPath: .status.lastAutomationRunTime
name: Last run
type: string
name: v1alpha2
schema:
openAPIV3Schema:
description: ImageUpdateAutomation is the Schema for the imageupdateautomations
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: ImageUpdateAutomationSpec defines the desired state of ImageUpdateAutomation
properties:
git:
description: GitSpec contains all the git-specific definitions. This
is technically optional, but in practice mandatory until there are
other kinds of source allowed.
properties:
checkout:
description: Checkout gives the parameters for cloning the git
repository, ready to make changes. If not present, the `spec.ref`
field from the referenced `GitRepository` or its default will
be used.
properties:
ref:
description: Reference gives a branch, tag or commit to clone
from the Git repository.
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
required:
- ref
type: object
commit:
description: Commit specifies how to commit to the git repository.
properties:
author:
description: Author gives the email and optionally the name
to use as the author of commits.
properties:
email:
description: Email gives the email to provide when making
a commit.
type: string
name:
description: Name gives the name to provide when making
a commit.
type: string
required:
- email
type: object
messageTemplate:
description: MessageTemplate provides a template for the commit
message, into which will be interpolated the details of
the change made.
type: string
signingKey:
description: SigningKey provides the option to sign commits
with a GPG key
properties:
secretRef:
description: SecretRef holds the name to a secret that
contains a 'git.asc' key corresponding to the ASCII
Armored file containing the GPG signing keypair as the
value. It must be in the same namespace as the ImageUpdateAutomation.
properties:
name:
description: Name of the referent
type: string
required:
- name
type: object
type: object
required:
- author
type: object
push:
description: Push specifies how and where to push commits made
by the automation. If missing, commits are pushed (back) to
`.spec.checkout.branch` or its default.
properties:
branch:
description: Branch specifies that commits should be pushed
to the branch named. The branch is created using `.spec.checkout.branch`
as the starting point, if it doesn't already exist.
type: string
required:
- branch
type: object
required:
- commit
type: object
interval:
description: Interval gives an lower bound for how often the automation
run should be attempted.
type: string
sourceRef:
description: SourceRef refers to the resource giving access details
to a git repository.
properties:
apiVersion:
description: API version of the referent
type: string
kind:
default: GitRepository
description: Kind of the referent
enum:
- GitRepository
type: string
name:
description: Name of the referent
type: string
required:
- kind
- name
type: object
suspend:
description: Suspend tells the controller to not run this automation,
until it is unset (or set to false). Defaults to false.
type: boolean
update:
default:
strategy: Setters
description: Update gives the specification for how to update the
files in the repository. This can be left empty, to use the default
value.
properties:
path:
description: Path to the directory containing the manifests to
be updated. Defaults to 'None', which translates to the root
path of the GitRepositoryRef.
type: string
strategy:
default: Setters
description: Strategy names the strategy to be used.
enum:
- Setters
type: string
required:
- strategy
type: object
required:
- interval
- sourceRef
type: object
status:
description: ImageUpdateAutomationStatus defines the observed state of
ImageUpdateAutomation
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,
type FooStatus struct{ // Represents the observations of a
foo's current state. // Known .status.conditions.type are:
\"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type
\ // +patchStrategy=merge // +listType=map // +listMapKey=type
\ Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
\n // other fields }"
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
lastAutomationRunTime:
description: LastAutomationRunTime records the last time the controller
ran this automation through to completion (even if no updates were
made).
format: date-time
type: string
lastHandledReconcileAt:
description: LastHandledReconcileAt holds the value of the most recent
reconcile request value, so a change can be detected.
type: string
lastPushCommit:
description: LastPushCommit records the SHA1 of the last commit made
by the controller, for this automation object
type: string
lastPushTime:
description: LastPushTime records the time of the last pushed change.
format: date-time
type: string
observedGeneration:
format: int64
type: integer
type: object
type: object
served: true
storage: false
subresources:
status: {}
- additionalPrinterColumns:
- jsonPath: .status.lastAutomationRunTime
name: Last run
type: string
name: v1beta1
schema:
openAPIV3Schema:
description: ImageUpdateAutomation is the Schema for the imageupdateautomations
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: ImageUpdateAutomationSpec defines the desired state of ImageUpdateAutomation
properties:
git:
description: GitSpec contains all the git-specific definitions. This
is technically optional, but in practice mandatory until there are
other kinds of source allowed.
properties:
checkout:
description: Checkout gives the parameters for cloning the git
repository, ready to make changes. If not present, the `spec.ref`
field from the referenced `GitRepository` or its default will
be used.
properties:
ref:
description: Reference gives a branch, tag or commit to clone
from the Git repository.
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
required:
- ref
type: object
commit:
description: Commit specifies how to commit to the git repository.
properties:
author:
description: Author gives the email and optionally the name
to use as the author of commits.
properties:
email:
description: Email gives the email to provide when making
a commit.
type: string
name:
description: Name gives the name to provide when making
a commit.
type: string
required:
- email
type: object
messageTemplate:
description: MessageTemplate provides a template for the commit
message, into which will be interpolated the details of
the change made.
type: string
signingKey:
description: SigningKey provides the option to sign commits
with a GPG key
properties:
secretRef:
description: SecretRef holds the name to a secret that
contains a 'git.asc' key corresponding to the ASCII
Armored file containing the GPG signing keypair as the
value. It must be in the same namespace as the ImageUpdateAutomation.
properties:
name:
description: Name of the referent
type: string
required:
- name
type: object
type: object
required:
- author
type: object
push:
description: Push specifies how and where to push commits made
by the automation. If missing, commits are pushed (back) to
`.spec.checkout.branch` or its default.
properties:
branch:
description: Branch specifies that commits should be pushed
to the branch named. The branch is created using `.spec.checkout.branch`
as the starting point, if it doesn't already exist.
type: string
required:
- branch
type: object
required:
- commit
type: object
interval:
description: Interval gives an lower bound for how often the automation
run should be attempted.
type: string
sourceRef:
description: SourceRef refers to the resource giving access details
to a git repository.
properties:
apiVersion:
description: API version of the referent
type: string
kind:
default: GitRepository
description: Kind of the referent
enum:
- GitRepository
type: string
name:
description: Name of the referent
type: string
required:
- kind
- name
type: object
suspend:
description: Suspend tells the controller to not run this automation,
until it is unset (or set to false). Defaults to false.
type: boolean
update:
default:
strategy: Setters
description: Update gives the specification for how to update the
files in the repository. This can be left empty, to use the default
value.
properties:
path:
description: Path to the directory containing the manifests to
be updated. Defaults to 'None', which translates to the root
path of the GitRepositoryRef.
type: string
strategy:
default: Setters
description: Strategy names the strategy to be used.
enum:
- Setters
type: string
required:
- strategy
type: object
required:
- interval
- sourceRef
type: object
status:
description: ImageUpdateAutomationStatus defines the observed state of
ImageUpdateAutomation
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,
type FooStatus struct{ // Represents the observations of a
foo's current state. // Known .status.conditions.type are:
\"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type
\ // +patchStrategy=merge // +listType=map // +listMapKey=type
\ Conditions []metav1.Condition `json:\"conditions,omitempty\"
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`
\n // other fields }"
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
lastAutomationRunTime:
description: LastAutomationRunTime records the last time the controller
ran this automation through to completion (even if no updates were
made).
format: date-time
type: string
lastHandledReconcileAt:
description: LastHandledReconcileAt holds the value of the most recent
reconcile request value, so a change can be detected.
type: string
lastPushCommit:
description: LastPushCommit records the SHA1 of the last commit made
by the controller, for this automation object
type: string
lastPushTime:
description: LastPushTime records the time of the last pushed change.
format: date-time
type: string
observedGeneration:
format: int64
type: integer
type: object
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ImageUpdateAutomation
listKind: ImageUpdateAutomationList
plural: imageupdateautomations
singular: imageupdateautomation
conditions:
- lastTransitionTime: "2022-02-09T20:58:20Z"
message: no conflicts found
reason: NoConflicts
status: "True"
type: NamesAccepted
- lastTransitionTime: "2022-02-09T20:58:20Z"
message: the initial names have been accepted
reason: InitialNamesAccepted
status: "True"
type: Established
storedVersions:
- v1beta1
Your cluster is way behind app.kubernetes.io/version: v0.24.1
, you need to update to v0.26.2.
Yep, fair point. The upgrade did get me past that issue thanks.
After many more refactoring, can confirm while your suggestion is very useful for those looking to run a single image policy per service per cluster, it does not work where there are many instances within the same cluster.
I had hoped that by refactoring the ImagePolicy, ImageRepository and ImageUpdateAutomation from flux-system namespace, to tenant environment namespace (<tenant>-staging
, <tenant>-uat
), I would be able to build on your idea of a common policy name.
However a better mind than mine would have noted without the need for two days refactoring, that the namespace is part of the inline image policy marker, and as such falls to the same merge issue described above.
Supposing
podinfo
supported release branches (which I recommend against, but lots of places do use), then you might have a test environment setup with image automation defining inline image policy marker comments such as followsnewTag: release-dcd9b99-1643898245 # {"$imagepolicy": "flux-system:podinfo-release:tag"}
To deploy to production, these image IDs would be merged from the release branch, to the main.
In addition to supporting release branch based deployments,
podinfo
maintainers would also like Flux to support pain free hotfixes, with a view to using this for continuous delivery on other services. Then you might have an production environment setup with image automation defining inline image policy marker comments such as follows.newTag: main-dcd9b99-1643898245 # {"$imagepolicy": "flux-system:podinfo-main:tag"}
However by merging the
release
branch tomain
, the inline image policy marker comment is erroneously updated to frompodinfo-main
topodinfo-release
. The goal is to havenewTag: release-dcd9b99-1643898245 # {"$imagepolicy": "flux-system:podinfo-main:tag"}
See the tag is now set to
release-dcd9b99-1643898245
, but the image policy will continue to identify hotfixes frompodinfo-main
.It would be better not to use inline comments