<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Flux – Toolkit Dev Guides</title><link>https://deploy-preview-2578--fluxcd.netlify.app/flux/gitops-toolkit/</link><description>Recent content in Toolkit Dev Guides on Flux</description><generator>Hugo -- gohugo.io</generator><language>en</language><atom:link href="https://deploy-preview-2578--fluxcd.netlify.app/flux/gitops-toolkit/index.xml" rel="self" type="application/rss+xml"/><item><title>Flux: Using the GitOps Toolkit APIs with Go</title><link>https://deploy-preview-2578--fluxcd.netlify.app/flux/gitops-toolkit/packages/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2578--fluxcd.netlify.app/flux/gitops-toolkit/packages/</guid><description>
&lt;p>While you can use the GitOps Toolkit APIs in a declarative manner with &lt;code>kubectl apply&lt;/code>,
we provide client library code for all our toolkit APIs that makes it easier to access them from Go.&lt;/p>
&lt;h2 id="go-packages">Go Packages&lt;/h2>
&lt;p>The GitOps Toolkit Go modules and controllers are released by following the
&lt;a href="https://semver.org" target="_blank">semver&lt;/a> conventions.&lt;/p>
&lt;p>The API schema definitions modules have the following dependencies:&lt;/p>
&lt;ul>
&lt;li>
&lt;a href="https://pkg.go.dev/github.com/fluxcd/pkg/apis/meta" target="_blank">github.com/fluxcd/pkg/apis/meta&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://pkg.go.dev/github.com/fluxcd/pkg/runtime" target="_blank">github.com/fluxcd/pkg/runtime&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://pkg.go.dev/k8s.io/apimachinery" target="_blank">k8s.io/apimachinery&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://pkg.go.dev/sigs.k8s.io/controller-runtime" target="_blank">sigs.k8s.io/controller-runtime&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>The APIs can be consumed with the
&lt;a href="https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/client" target="_blank">controller-runtime client&lt;/a>.&lt;/p>
&lt;h3 id="sourcetoolkitfluxcdio">source.toolkit.fluxcd.io&lt;/h3>
&lt;p>Download package&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>go get github.com/fluxcd/source-controller/api
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Import package&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> sourcev1 &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/source-controller/api/v1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>API Types&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Name&lt;/th>
&lt;th>Version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/source/gitrepositories/">GitRepository&lt;/a>&lt;/td>
&lt;td>v1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/source/helmrepositories/">HelmRepository&lt;/a>&lt;/td>
&lt;td>v1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/source/helmcharts/">HelmChart&lt;/a>&lt;/td>
&lt;td>v1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/source/ocirepositories/">OCIRepository&lt;/a>&lt;/td>
&lt;td>v1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/source/buckets/">Bucket&lt;/a>&lt;/td>
&lt;td>v1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/source/externalartifacts/">ExternalArtifact&lt;/a>&lt;/td>
&lt;td>v1&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="kustomizetoolkitfluxcdio">kustomize.toolkit.fluxcd.io&lt;/h3>
&lt;p>Download package&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>go get github.com/fluxcd/kustomize-controller/api
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Import package&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> kustomizev1 &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/kustomize-controller/api/v1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>API Types&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Name&lt;/th>
&lt;th>Version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/kustomize/kustomizations/">Kustomization&lt;/a>&lt;/td>
&lt;td>v1&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="helmtoolkitfluxcdio">helm.toolkit.fluxcd.io&lt;/h3>
&lt;p>Download package&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>go get github.com/fluxcd/helm-controller/api
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Import package&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> helmv2 &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/helm-controller/api/v2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>API Types&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Name&lt;/th>
&lt;th>Version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/helm/helmreleases/">HelmRelease&lt;/a>&lt;/td>
&lt;td>v2&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="notificationtoolkitfluxcdio">notification.toolkit.fluxcd.io&lt;/h3>
&lt;p>Download package&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>go get github.com/fluxcd/notification-controller/api
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Import package&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> notificationv1b3 &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/notification-controller/api/v1beta3&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>and for &lt;code>Receiver&lt;/code> objects:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> notificationv1 &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/notification-controller/api/v1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>API Types&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Name&lt;/th>
&lt;th>Version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/notification/receivers/">Receiver&lt;/a>&lt;/td>
&lt;td>v1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/notification/providers/">Provider&lt;/a>&lt;/td>
&lt;td>v1beta3&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/notification/alerts/">Alert&lt;/a>&lt;/td>
&lt;td>v1beta3&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="imagetoolkitfluxcdio">image.toolkit.fluxcd.io&lt;/h3>
&lt;p>Download package&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>go get github.com/fluxcd/image-reflector-controller/api
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>go get github.com/fluxcd/image-automation-controller/api
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Import package&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> imagev1 &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/image-reflector-controller/api/v1beta2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> autov1 &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/image-automation-controller/api/v1beta2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>API Types&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Name&lt;/th>
&lt;th>Version&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/image/imagerepositories/">ImageRepository&lt;/a>&lt;/td>
&lt;td>v1beta2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/image/imagepolicies/">ImagePolicy&lt;/a>&lt;/td>
&lt;td>v1beta2&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/image/imageupdateautomations/">ImageUpdateAutomation&lt;/a>&lt;/td>
&lt;td>v1beta2&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="crud-example">CRUD Example&lt;/h2>
&lt;p>Here is an example of how to create a Helm release, wait for it to install, then delete it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">package&lt;/span> main
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;context&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;fmt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;time&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> apiextensionsv1 &lt;span style="color:#4070a0">&amp;#34;k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;k8s.io/apimachinery/pkg/api/meta&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metav1 &lt;span style="color:#4070a0">&amp;#34;k8s.io/apimachinery/pkg/apis/meta/v1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;k8s.io/apimachinery/pkg/runtime&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;k8s.io/apimachinery/pkg/types&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;k8s.io/apimachinery/pkg/util/wait&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _ &lt;span style="color:#4070a0">&amp;#34;k8s.io/client-go/plugin/pkg/client/auth&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ctrl &lt;span style="color:#4070a0">&amp;#34;sigs.k8s.io/controller-runtime&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;sigs.k8s.io/controller-runtime/pkg/client&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> helmv2 &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/helm-controller/api/v2&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> apimeta &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/pkg/apis/meta&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sourcev1 &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/source-controller/api/v1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">func&lt;/span> &lt;span style="color:#06287e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// register the GitOps Toolkit schema definitions
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> scheme &lt;span style="color:#666">:=&lt;/span> runtime.&lt;span style="color:#06287e">NewScheme&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _ = sourcev1.&lt;span style="color:#06287e">AddToScheme&lt;/span>(scheme)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _ = helmv2.&lt;span style="color:#06287e">AddToScheme&lt;/span>(scheme)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// init Kubernetes client
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> kubeClient, err &lt;span style="color:#666">:=&lt;/span> client.&lt;span style="color:#06287e">New&lt;/span>(ctrl.&lt;span style="color:#06287e">GetConfigOrDie&lt;/span>(), client.Options{Scheme: scheme})
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">panic&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// set a deadline for the Kubernetes API operations
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> ctx, cancel &lt;span style="color:#666">:=&lt;/span> context.&lt;span style="color:#06287e">WithTimeout&lt;/span>(context.&lt;span style="color:#06287e">Background&lt;/span>(), &lt;span style="color:#40a070">60&lt;/span>&lt;span style="color:#666">*&lt;/span>time.Second)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">defer&lt;/span> &lt;span style="color:#06287e">cancel&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// create a Helm repository pointing to Bitnami
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> helmRepository &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#666">&amp;amp;&lt;/span>sourcev1.HelmRepository{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ObjectMeta: metav1.ObjectMeta{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Name: &lt;span style="color:#4070a0">&amp;#34;bitnami&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Namespace: &lt;span style="color:#4070a0">&amp;#34;default&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Spec: sourcev1.HelmRepositorySpec{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> URL: &lt;span style="color:#4070a0">&amp;#34;https://charts.bitnami.com/bitnami&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Interval: metav1.Duration{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Duration: &lt;span style="color:#40a070">30&lt;/span> &lt;span style="color:#666">*&lt;/span> time.Minute,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">:=&lt;/span> kubeClient.&lt;span style="color:#06287e">Create&lt;/span>(ctx, helmRepository); err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> fmt.&lt;span style="color:#06287e">Println&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#007020;font-weight:bold">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> fmt.&lt;span style="color:#06287e">Println&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;HelmRepository bitnami created&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// create a Helm release for nginx
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> helmRelease &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#666">&amp;amp;&lt;/span>helmv2.HelmRelease{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ObjectMeta: metav1.ObjectMeta{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Name: &lt;span style="color:#4070a0">&amp;#34;nginx&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Namespace: &lt;span style="color:#4070a0">&amp;#34;default&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Spec: helmv2.HelmReleaseSpec{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ReleaseName: &lt;span style="color:#4070a0">&amp;#34;nginx&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Interval: metav1.Duration{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Duration: &lt;span style="color:#40a070">5&lt;/span> &lt;span style="color:#666">*&lt;/span> time.Minute,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Chart: helmv2.HelmChartTemplate{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Spec: helmv2.HelmChartTemplateSpec{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Chart: &lt;span style="color:#4070a0">&amp;#34;nginx&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Version: &lt;span style="color:#4070a0">&amp;#34;8.x&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> SourceRef: helmv2.CrossNamespaceObjectReference{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Kind: sourcev1.HelmRepositoryKind,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Name: &lt;span style="color:#4070a0">&amp;#34;bitnami&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Values: &lt;span style="color:#666">&amp;amp;&lt;/span>apiextensionsv1.JSON{Raw: []&lt;span style="color:#007020">byte&lt;/span>(&lt;span style="color:#4070a0">`{&amp;#34;service&amp;#34;: {&amp;#34;type&amp;#34;: &amp;#34;ClusterIP&amp;#34;}}`&lt;/span>)},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">:=&lt;/span> kubeClient.&lt;span style="color:#06287e">Create&lt;/span>(ctx, helmRelease); err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> fmt.&lt;span style="color:#06287e">Println&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#007020;font-weight:bold">else&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> fmt.&lt;span style="color:#06287e">Println&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;HelmRelease nginx created&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// wait for the a Helm release to be reconciled
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> fmt.&lt;span style="color:#06287e">Println&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;Waiting for nginx to be installed&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">:=&lt;/span> wait.&lt;span style="color:#06287e">PollImmediate&lt;/span>(&lt;span style="color:#40a070">2&lt;/span>&lt;span style="color:#666">*&lt;/span>time.Second, &lt;span style="color:#40a070">1&lt;/span>&lt;span style="color:#666">*&lt;/span>time.Minute,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">func&lt;/span>() (done &lt;span style="color:#902000">bool&lt;/span>, err &lt;span style="color:#902000">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespacedName &lt;span style="color:#666">:=&lt;/span> types.NamespacedName{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Namespace: helmRelease.&lt;span style="color:#06287e">GetNamespace&lt;/span>(),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Name: helmRelease.&lt;span style="color:#06287e">GetName&lt;/span>(),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">:=&lt;/span> kubeClient.&lt;span style="color:#06287e">Get&lt;/span>(ctx, namespacedName, helmRelease); err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> &lt;span style="color:#007020;font-weight:bold">false&lt;/span>, err
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> meta.&lt;span style="color:#06287e">IsStatusConditionTrue&lt;/span>(helmRelease.Status.Conditions, apimeta.ReadyCondition), &lt;span style="color:#007020;font-weight:bold">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }); err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> fmt.&lt;span style="color:#06287e">Println&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// print the reconciliation status
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> fmt.&lt;span style="color:#06287e">Println&lt;/span>(meta.&lt;span style="color:#06287e">FindStatusCondition&lt;/span>(helmRelease.Status.Conditions, apimeta.ReadyCondition).Message)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// uninstall the release and delete the repository
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">:=&lt;/span> kubeClient.&lt;span style="color:#06287e">Delete&lt;/span>(ctx, helmRelease); err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> fmt.&lt;span style="color:#06287e">Println&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">:=&lt;/span> kubeClient.&lt;span style="color:#06287e">Delete&lt;/span>(ctx, helmRepository); err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> fmt.&lt;span style="color:#06287e">Println&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> fmt.&lt;span style="color:#06287e">Println&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;Helm repository and release deleted&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For an example on how to build a Kubernetes controller that interacts with the GitOps Toolkit APIs see
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/gitops-toolkit/source-watcher/">source-watcher&lt;/a>.&lt;/p>
&lt;h2 id="artifact-sdk">Artifact SDK&lt;/h2>
&lt;p>The Artifact SDK provides comprehensive functionality for packaging, storing,
serving, and processing Flux artifacts. It is the foundation for building
3rd-party controllers that manage &lt;code>ExternalArtifact&lt;/code> resources.&lt;/p>
&lt;p>Download package&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>go get github.com/fluxcd/pkg/artifact
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Import packages&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/pkg/artifact/config&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/pkg/artifact/server&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/pkg/artifact/storage&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/pkg/artifact/digest&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Sub-package&lt;/th>
&lt;th>Purpose&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>
&lt;a href="https://pkg.go.dev/github.com/fluxcd/pkg/artifact/config" target="_blank">config&lt;/a>&lt;/td>
&lt;td>Flag binding and configuration for storage, server, retention, and digest options&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>
&lt;a href="https://pkg.go.dev/github.com/fluxcd/pkg/artifact/server" target="_blank">server&lt;/a>&lt;/td>
&lt;td>HTTP file server with graceful shutdown for serving artifacts in-cluster&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>
&lt;a href="https://pkg.go.dev/github.com/fluxcd/pkg/artifact/storage" target="_blank">storage&lt;/a>&lt;/td>
&lt;td>Artifact lifecycle management — create, archive, verify, copy, GC&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>
&lt;a href="https://pkg.go.dev/github.com/fluxcd/pkg/artifact/digest" target="_blank">digest&lt;/a>&lt;/td>
&lt;td>Multi-algorithm digest computation (SHA256, SHA512, BLAKE3)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>For a step-by-step guide on building a controller using the SDK, see
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/gitops-toolkit/external-artifact-sdk/">Building ExternalArtifact Controllers&lt;/a>.&lt;/p></description></item><item><title>Flux: Watching for source changes</title><link>https://deploy-preview-2578--fluxcd.netlify.app/flux/gitops-toolkit/source-watcher/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2578--fluxcd.netlify.app/flux/gitops-toolkit/source-watcher/</guid><description>
&lt;p>In this guide you&amp;rsquo;ll be developing a Kubernetes controller with
&lt;a href="https://github.com/kubernetes-sigs/kubebuilder" target="_blank">Kubebuilder&lt;/a>
that subscribes to
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/source/gitrepositories/">GitRepository&lt;/a>
events and reacts to revision changes by downloading the artifact produced by
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/source/">source-controller&lt;/a>.&lt;/p>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>On your dev machine install the following tools:&lt;/p>
&lt;ul>
&lt;li>go &amp;gt;= 1.22&lt;/li>
&lt;li>kubebuilder &amp;gt;= 3.0&lt;/li>
&lt;li>kind &amp;gt;= 0.22&lt;/li>
&lt;li>kubectl &amp;gt;= 1.29&lt;/li>
&lt;/ul>
&lt;h2 id="install-flux">Install Flux&lt;/h2>
&lt;p>Install the Flux CLI with Homebrew on macOS or Linux:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>brew install fluxcd/tap/flux
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Create a cluster for testing:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>kind create cluster --name dev
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Verify that your dev machine satisfies the prerequisites with:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>flux check --pre
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Install source-controller on the dev cluster:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>flux install &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span>--namespace&lt;span style="color:#666">=&lt;/span>flux-system &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span>--network-policy&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#007020">false&lt;/span> &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span>--components&lt;span style="color:#666">=&lt;/span>source-controller
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="clone-the-sample-controller">Clone the sample controller&lt;/h2>
&lt;p>You&amp;rsquo;ll be using
&lt;a href="https://github.com/fluxcd/source-watcher" target="_blank">fluxcd/source-watcher&lt;/a> as
a template for developing your own controller. The source-watcher was scaffolded with &lt;code>kubebuilder init&lt;/code>.&lt;/p>
&lt;p>Clone the source-watcher repository:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>git clone https://github.com/fluxcd/source-watcher
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">cd&lt;/span> source-watcher
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git checkout release/v1.3.x
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Build the controller:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>make
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="run-the-controller">Run the controller&lt;/h2>
&lt;p>Port forward to source-controller artifacts server:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>kubectl -n flux-system port-forward svc/source-controller 8181:80
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Export the local address as &lt;code>SOURCE_CONTROLLER_LOCALHOST&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">export&lt;/span> &lt;span style="color:#bb60d5">SOURCE_CONTROLLER_LOCALHOST&lt;/span>&lt;span style="color:#666">=&lt;/span>localhost:8181
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Run source-watcher locally:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>make run
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Create a Git source:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>flux create &lt;span style="color:#007020">source&lt;/span> git &lt;span style="color:#007020">test&lt;/span> &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span>--url&lt;span style="color:#666">=&lt;/span>https://github.com/fluxcd/flux2 &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span>--ignore-paths&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">&amp;#39;/*,!/manifests&amp;#39;&lt;/span> &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span>--tag&lt;span style="color:#666">=&lt;/span>v2.2.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The source-watcher will log the revision:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#666">{&lt;/span>&lt;span style="color:#4070a0">&amp;#34;level&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;info&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;ts&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;2024-05-14T16:43:42.703+0200&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;msg&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;New revision detected&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;controller&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;gitrepository&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;controllerGroup&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;source.toolkit.fluxcd.io&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;controllerKind&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;GitRepository&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;GitRepository&amp;#34;&lt;/span>:&lt;span style="color:#666">{&lt;/span>&lt;span style="color:#4070a0">&amp;#34;name&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;test&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;namespace&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;flux-system&amp;#34;&lt;/span>&lt;span style="color:#666">}&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;namespace&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;flux-system&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;name&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;test&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;reconcileID&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;ef0fe80e-3952-4835-ae9d-01760c4eadde&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;revision&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;v2.2.0@sha1:81606709114f6d16a432f9f4bfc774942f054327&amp;#34;&lt;/span>&lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Change the Git tag:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>flux create &lt;span style="color:#007020">source&lt;/span> git &lt;span style="color:#007020">test&lt;/span> &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span>--url&lt;span style="color:#666">=&lt;/span>https://github.com/fluxcd/flux2 &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span>--ignore-paths&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">&amp;#39;/*,!/manifests&amp;#39;&lt;/span> &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span>--tag&lt;span style="color:#666">=&lt;/span>v2.3.0
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And source-watcher will log the new revision:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#666">{&lt;/span>&lt;span style="color:#4070a0">&amp;#34;level&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;info&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;ts&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;2024-05-14T16:51:33.499+0200&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;msg&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;New revision detected&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;controller&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;gitrepository&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;controllerGroup&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;source.toolkit.fluxcd.io&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;controllerKind&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;GitRepository&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;GitRepository&amp;#34;&lt;/span>:&lt;span style="color:#666">{&lt;/span>&lt;span style="color:#4070a0">&amp;#34;name&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;test&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;namespace&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;flux-system&amp;#34;&lt;/span>&lt;span style="color:#666">}&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;namespace&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;flux-system&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;name&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;test&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;reconcileID&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;cc0f83bb-b7a0-4c19-a254-af9962ae39cd&amp;#34;&lt;/span>,&lt;span style="color:#4070a0">&amp;#34;revision&amp;#34;&lt;/span>:&lt;span style="color:#4070a0">&amp;#34;v2.3.0@sha1:658925c2c0e6c408597d907a8ebee06a9a6d7f30&amp;#34;&lt;/span>&lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The source-controller reports the revision under &lt;code>GitRepository.Status.Artifact.Revision&lt;/code> in the format: &lt;code>&amp;lt;branch|tag&amp;gt;@sha1:&amp;lt;commit&amp;gt;&lt;/code>.&lt;/p>
&lt;h2 id="how-it-works">How it works&lt;/h2>
&lt;p>The
&lt;a href="https://github.com/fluxcd/source-watcher/blob/main/controllers/gitrepository_watcher.go" target="_blank">GitRepositoryWatcher&lt;/a>
controller does the following:&lt;/p>
&lt;ul>
&lt;li>subscribes to &lt;code>GitRepository&lt;/code> events&lt;/li>
&lt;li>detects when the Git revision changes&lt;/li>
&lt;li>downloads and extracts the source artifact&lt;/li>
&lt;li>writes the extracted dir names to stdout&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">type&lt;/span> GitRepositoryWatcher &lt;span style="color:#007020;font-weight:bold">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> client.Client
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> HttpRetry &lt;span style="color:#902000">int&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> artifactFetcher &lt;span style="color:#666">*&lt;/span>fetch.ArchiveFetcher
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">func&lt;/span> (r &lt;span style="color:#666">*&lt;/span>GitRepositoryWatcher) &lt;span style="color:#06287e">SetupWithManager&lt;/span>(mgr ctrl.Manager) &lt;span style="color:#902000">error&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> r.artifactFetcher = fetch.&lt;span style="color:#06287e">NewArchiveFetcher&lt;/span>(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> r.HttpRetry,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tar.UnlimitedUntarSize,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> os.&lt;span style="color:#06287e">Getenv&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;SOURCE_CONTROLLER_LOCALHOST&amp;#34;&lt;/span>),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> )
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> ctrl.&lt;span style="color:#06287e">NewControllerManagedBy&lt;/span>(mgr).
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#06287e">For&lt;/span>(&lt;span style="color:#666">&amp;amp;&lt;/span>sourcev1.GitRepository{}, builder.&lt;span style="color:#06287e">WithPredicates&lt;/span>(GitRepositoryRevisionChangePredicate{})).
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#06287e">Complete&lt;/span>(r)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=gitrepositories,verbs=get;list;watch
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// +kubebuilder:rbac:groups=source.toolkit.fluxcd.io,resources=gitrepositories/status,verbs=get
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">func&lt;/span> (r &lt;span style="color:#666">*&lt;/span>GitRepositoryWatcher) &lt;span style="color:#06287e">Reconcile&lt;/span>(ctx context.Context, req ctrl.Request) (ctrl.Result, &lt;span style="color:#902000">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log &lt;span style="color:#666">:=&lt;/span> ctrl.&lt;span style="color:#06287e">LoggerFrom&lt;/span>(ctx)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// get source object
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> &lt;span style="color:#007020;font-weight:bold">var&lt;/span> repository sourcev1.GitRepository
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">:=&lt;/span> r.&lt;span style="color:#06287e">Get&lt;/span>(ctx, req.NamespacedName, &lt;span style="color:#666">&amp;amp;&lt;/span>repository); err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> ctrl.Result{}, client.&lt;span style="color:#06287e">IgnoreNotFound&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> artifact &lt;span style="color:#666">:=&lt;/span> repository.Status.Artifact
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#06287e">Info&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;New revision detected&amp;#34;&lt;/span>, &lt;span style="color:#4070a0">&amp;#34;revision&amp;#34;&lt;/span>, artifact.Revision)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// create tmp dir
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> tmpDir, err &lt;span style="color:#666">:=&lt;/span> os.&lt;span style="color:#06287e">MkdirTemp&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;&amp;#34;&lt;/span>, repository.Name)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> ctrl.Result{}, fmt.&lt;span style="color:#06287e">Errorf&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;failed to create temp dir, error: %w&amp;#34;&lt;/span>, err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">defer&lt;/span> os.&lt;span style="color:#06287e">RemoveAll&lt;/span>(tmpDir)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// download and extract artifact
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">:=&lt;/span> r.artifactFetcher.&lt;span style="color:#06287e">Fetch&lt;/span>(artifact.URL, artifact.Digest, tmpDir); err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#06287e">Error&lt;/span>(err, &lt;span style="color:#4070a0">&amp;#34;unable to fetch artifact&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> ctrl.Result{}, err
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// list artifact content
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> files, err &lt;span style="color:#666">:=&lt;/span> os.&lt;span style="color:#06287e">ReadDir&lt;/span>(tmpDir)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> ctrl.Result{}, fmt.&lt;span style="color:#06287e">Errorf&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;failed to list files, error: %w&amp;#34;&lt;/span>, err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// do something with the artifact content
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> &lt;span style="color:#007020;font-weight:bold">for&lt;/span> _, f &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#007020;font-weight:bold">range&lt;/span> files {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#06287e">Info&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;Processing &amp;#34;&lt;/span> &lt;span style="color:#666">+&lt;/span> f.&lt;span style="color:#06287e">Name&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> ctrl.Result{}, &lt;span style="color:#007020;font-weight:bold">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To add the watcher to an existing project, copy the controller and the revision change predicate to your &lt;code>controllers&lt;/code> dir:&lt;/p>
&lt;ul>
&lt;li>
&lt;a href="https://github.com/fluxcd/source-watcher/blob/main/controllers/gitrepository_watcher.go" target="_blank">gitrepository_watcher.go&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://github.com/fluxcd/source-watcher/blob/main/controllers/gitrepository_predicate.go" target="_blank">gitrepository_predicate.go&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>In your &lt;code>main.go&lt;/code> init function, register the Source API schema:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> utilruntime &lt;span style="color:#4070a0">&amp;#34;k8s.io/apimachinery/pkg/util/runtime&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> clientgoscheme &lt;span style="color:#4070a0">&amp;#34;k8s.io/client-go/kubernetes/scheme&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sourcev1 &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/source-controller/api/v1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">func&lt;/span> &lt;span style="color:#06287e">init&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> utilruntime.&lt;span style="color:#06287e">Must&lt;/span>(clientgoscheme.&lt;span style="color:#06287e">AddToScheme&lt;/span>(scheme))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> utilruntime.&lt;span style="color:#06287e">Must&lt;/span>(sourcev1.&lt;span style="color:#06287e">AddToScheme&lt;/span>(scheme)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// +kubebuilder:scaffold:scheme
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Start the controller in the main function:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">func&lt;/span> &lt;span style="color:#06287e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err = (&lt;span style="color:#666">&amp;amp;&lt;/span>controllers.GitRepositoryWatcher{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Client: mgr.&lt;span style="color:#06287e">GetClient&lt;/span>(),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> HttpRetry: httpRetry,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }).&lt;span style="color:#06287e">SetupWithManager&lt;/span>(mgr); err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> setupLog.&lt;span style="color:#06287e">Error&lt;/span>(err, &lt;span style="color:#4070a0">&amp;#34;unable to create controller&amp;#34;&lt;/span>, &lt;span style="color:#4070a0">&amp;#34;controller&amp;#34;&lt;/span>, &lt;span style="color:#4070a0">&amp;#34;GitRepositoryWatcher&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> os.&lt;span style="color:#06287e">Exit&lt;/span>(&lt;span style="color:#40a070">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that the watcher depends on Flux
&lt;a href="https://pkg.go.dev/github.com/fluxcd/pkg/runtime" target="_blank">runtime&lt;/a>
and Kubernetes
&lt;a href="https://pkg.go.dev/sigs.k8s.io/controller-runtime" target="_blank">controller-runtime&lt;/a>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#06287e">require&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> github.com&lt;span style="color:#666">/&lt;/span>fluxcd&lt;span style="color:#666">/&lt;/span>pkg&lt;span style="color:#666">/&lt;/span>runtime v0&lt;span style="color:#40a070">.47.1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sigs.k8s.io&lt;span style="color:#666">/&lt;/span>controller&lt;span style="color:#666">-&lt;/span>runtime v0&lt;span style="color:#40a070">.18.2&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>That&amp;rsquo;s it! Happy hacking!&lt;/p>
&lt;div class="alert alert-info" role="alert">
&lt;h4 class="alert-heading">Building artifact generators&lt;/h4>
If you want to build a controller that &lt;strong>generates&lt;/strong> artifacts (not just watches them),
see
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/gitops-toolkit/external-artifact-sdk/">Building ExternalArtifact Controllers&lt;/a> for a guide
on using the Flux Artifact SDK to create &lt;code>ExternalArtifact&lt;/code> resources that can be
consumed by &lt;code>kustomize-controller&lt;/code> and &lt;code>helm-controller&lt;/code>.
&lt;/div></description></item><item><title>Flux: Building ExternalArtifact Controllers</title><link>https://deploy-preview-2578--fluxcd.netlify.app/flux/gitops-toolkit/external-artifact-sdk/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2578--fluxcd.netlify.app/flux/gitops-toolkit/external-artifact-sdk/</guid><description>
&lt;p>In this guide you&amp;rsquo;ll learn how to build a Kubernetes controller that acts as a
3rd-party source of truth for Flux by creating
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/source/externalartifacts/">ExternalArtifact&lt;/a> resources.
Your controller will use the Flux Artifact SDK
(
&lt;a href="https://pkg.go.dev/github.com/fluxcd/pkg/artifact" target="_blank">&lt;code>github.com/fluxcd/pkg/artifact&lt;/code>&lt;/a>)
to package, store, and serve artifacts that can be consumed by
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/kustomize/">kustomize-controller&lt;/a> and
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/helm/">helm-controller&lt;/a>.&lt;/p>
&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>The &lt;code>ExternalArtifact&lt;/code> API (part of
&lt;a href="https://github.com/fluxcd/flux2/blob/main/rfcs/0012-external-artifact/README.md" target="_blank">RFC-0012&lt;/a>)
allows 3rd-party controllers to expose artifacts in-cluster in the same way
&lt;code>source-controller&lt;/code> does. This means Flux &lt;code>Kustomization&lt;/code> and &lt;code>HelmRelease&lt;/code>
resources can reference your custom source types via &lt;code>ExternalArtifact&lt;/code>
without any changes to the Flux core.&lt;/p>
&lt;p>The Artifact SDK provides four sub-packages:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Package&lt;/th>
&lt;th>Import Path&lt;/th>
&lt;th>Purpose&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>config&lt;/strong>&lt;/td>
&lt;td>&lt;code>github.com/fluxcd/pkg/artifact/config&lt;/code>&lt;/td>
&lt;td>Flag binding and configuration for storage, server, retention, and digest options&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>server&lt;/strong>&lt;/td>
&lt;td>&lt;code>github.com/fluxcd/pkg/artifact/server&lt;/code>&lt;/td>
&lt;td>HTTP file server with graceful shutdown for serving artifacts in-cluster&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>storage&lt;/strong>&lt;/td>
&lt;td>&lt;code>github.com/fluxcd/pkg/artifact/storage&lt;/code>&lt;/td>
&lt;td>Artifact lifecycle management — create, archive, verify, copy, GC&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>digest&lt;/strong>&lt;/td>
&lt;td>&lt;code>github.com/fluxcd/pkg/artifact/digest&lt;/code>&lt;/td>
&lt;td>Multi-algorithm digest computation (SHA1,SHA256, SHA512, BLAKE3)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>On your dev machine install the following tools:&lt;/p>
&lt;ul>
&lt;li>go &amp;gt;= 1.24&lt;/li>
&lt;li>kubebuilder &amp;gt;= 4.0&lt;/li>
&lt;li>kind &amp;gt;= 0.22&lt;/li>
&lt;li>kubectl &amp;gt;= 1.31&lt;/li>
&lt;li>Flux CLI &amp;gt;= 2.7&lt;/li>
&lt;/ul>
&lt;h2 id="install-flux">Install Flux&lt;/h2>
&lt;p>Create a cluster for testing:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>kind create cluster --name dev
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Install Flux with the &lt;code>ExternalArtifact&lt;/code> feature gate enabled:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>flux install &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> --namespace&lt;span style="color:#666">=&lt;/span>flux-system &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> --network-policy&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#007020">false&lt;/span> &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> --components&lt;span style="color:#666">=&lt;/span>source-controller,kustomize-controller,helm-controller
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Enable the &lt;code>ExternalArtifact&lt;/code> feature gate on &lt;code>kustomize-controller&lt;/code> and
&lt;code>helm-controller&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>kubectl -n flux-system patch deployment kustomize-controller &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> --type&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">&amp;#39;json&amp;#39;&lt;/span> -p&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">&amp;#39;[{&amp;#34;op&amp;#34;: &amp;#34;add&amp;#34;, &amp;#34;path&amp;#34;: &amp;#34;/spec/template/spec/containers/0/args/-&amp;#34;, &amp;#34;value&amp;#34;: &amp;#34;--feature-gates=ExternalArtifact=true&amp;#34;}]&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl -n flux-system patch deployment helm-controller &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> --type&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">&amp;#39;json&amp;#39;&lt;/span> -p&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#4070a0">&amp;#39;[{&amp;#34;op&amp;#34;: &amp;#34;add&amp;#34;, &amp;#34;path&amp;#34;: &amp;#34;/spec/template/spec/containers/0/args/-&amp;#34;, &amp;#34;value&amp;#34;: &amp;#34;--feature-gates=ExternalArtifact=true&amp;#34;}]&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="reference-implementation">Reference implementation&lt;/h2>
&lt;p>The
&lt;a href="https://github.com/fluxcd/source-watcher" target="_blank">fluxcd/source-watcher&lt;/a> repository
contains a full reference implementation (branch &lt;code>v2&lt;/code>) of an &lt;code>ArtifactGenerator&lt;/code>
controller that uses the ExternalArtifact API and SDK. Clone it to follow along:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>git clone https://github.com/fluxcd/source-watcher
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">cd&lt;/span> source-watcher
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git checkout v2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="sdk-quick-start">SDK Quick Start&lt;/h2>
&lt;h3 id="1-add-the-dependency">1. Add the dependency&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>go get github.com/fluxcd/pkg/artifact
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>go get github.com/fluxcd/source-controller/api
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="2-configure-the-artifact-server">2. Configure the artifact server&lt;/h3>
&lt;p>Use &lt;code>config.Options&lt;/code> to declare storage and server settings. The SDK provides
flag binding with environment variable support out of the box:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;github.com/spf13/pflag&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/pkg/artifact/config&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">func&lt;/span> &lt;span style="color:#06287e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> opts &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#666">&amp;amp;&lt;/span>config.Options{}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// Bind CLI flags for --storage-path, --storage-addr,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> &lt;span style="color:#60a0b0;font-style:italic">// --storage-adv-addr, --artifact-retention-ttl,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> &lt;span style="color:#60a0b0;font-style:italic">// --artifact-retention-records, --artifact-digest-algo.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> opts.&lt;span style="color:#06287e">BindFlags&lt;/span>(pflag.CommandLine)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> pflag.&lt;span style="color:#06287e">Parse&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Available configuration flags and their defaults:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Flag&lt;/th>
&lt;th>Env Var&lt;/th>
&lt;th>Default&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>--storage-path&lt;/code>&lt;/td>
&lt;td>&lt;code>STORAGE_PATH&lt;/code>&lt;/td>
&lt;td>&lt;code>/data&lt;/code>&lt;/td>
&lt;td>Directory where artifacts are stored&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--storage-addr&lt;/code>&lt;/td>
&lt;td>&lt;code>STORAGE_ADDRESS&lt;/code>&lt;/td>
&lt;td>&lt;code>:9090&lt;/code>&lt;/td>
&lt;td>Address the artifact server binds to&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--storage-adv-addr&lt;/code>&lt;/td>
&lt;td>&lt;code>STORAGE_ADV_ADDR&lt;/code>&lt;/td>
&lt;td>&lt;em>(auto)&lt;/em>&lt;/td>
&lt;td>In-cluster address advertised to clients&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--artifact-retention-ttl&lt;/code>&lt;/td>
&lt;td>—&lt;/td>
&lt;td>&lt;code>1m&lt;/code>&lt;/td>
&lt;td>Duration after which stale artifacts are GC&amp;rsquo;d&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--artifact-retention-records&lt;/code>&lt;/td>
&lt;td>—&lt;/td>
&lt;td>&lt;code>2&lt;/code>&lt;/td>
&lt;td>Max artifacts kept per source after GC&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--artifact-digest-algo&lt;/code>&lt;/td>
&lt;td>—&lt;/td>
&lt;td>&lt;code>sha256&lt;/code>&lt;/td>
&lt;td>Hashing algorithm for artifact digests&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="3-initialize-storage">3. Initialize Storage&lt;/h3>
&lt;p>The &lt;code>storage.Storage&lt;/code> type manages artifact tarballs on the local filesystem:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/pkg/artifact/config&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/pkg/artifact/storage&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// Create storage from configuration options.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>store, err &lt;span style="color:#666">:=&lt;/span> storage.&lt;span style="color:#06287e">New&lt;/span>(opts)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020">panic&lt;/span>(err)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="4-start-the-artifact-file-server">4. Start the artifact file server&lt;/h3>
&lt;p>Start the HTTP file server after the controller manager is elected leader.
The server exposes artifacts under the configured storage path and supports
graceful shutdown via context cancellation:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/pkg/artifact/server&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// Start the artifact server after the controller-manager receives leadership.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>&lt;span style="color:#007020;font-weight:bold">go&lt;/span> &lt;span style="color:#007020;font-weight:bold">func&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">&amp;lt;-&lt;/span>mgr.&lt;span style="color:#06287e">Elected&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">:=&lt;/span> server.&lt;span style="color:#06287e">Start&lt;/span>(ctx, opts); err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> setupLog.&lt;span style="color:#06287e">Error&lt;/span>(err, &lt;span style="color:#4070a0">&amp;#34;unable to start artifact server&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}()
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="5-create-and-archive-artifacts">5. Create and archive artifacts&lt;/h3>
&lt;p>In your controller&amp;rsquo;s &lt;code>Reconcile&lt;/code> function, use the storage API to create new
artifacts, archive directories into tarballs, and set digest/size metadata:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metav1 &lt;span style="color:#4070a0">&amp;#34;k8s.io/apimachinery/pkg/apis/meta/v1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/pkg/apis/meta&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/pkg/artifact/storage&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">func&lt;/span> (r &lt;span style="color:#666">*&lt;/span>ArtifactGeneratorReconciler) &lt;span style="color:#06287e">Reconcile&lt;/span>(ctx context.Context, req ctrl.Request) (ctrl.Result, &lt;span style="color:#902000">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// Create a new artifact descriptor.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> artifact &lt;span style="color:#666">:=&lt;/span> store.&lt;span style="color:#06287e">NewArtifactFor&lt;/span>(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;MySource&amp;#34;&lt;/span>, &lt;span style="color:#60a0b0;font-style:italic">// kind
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> &lt;span style="color:#666">&amp;amp;&lt;/span>mySourceObject.ObjectMeta, &lt;span style="color:#60a0b0;font-style:italic">// metadata (namespace + name)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> revision, &lt;span style="color:#60a0b0;font-style:italic">// e.g. &amp;#34;v1.0.0@sha256:abc123...&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> fmt.&lt;span style="color:#06287e">Sprintf&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;%s.tar.gz&amp;#34;&lt;/span>, hash),&lt;span style="color:#60a0b0;font-style:italic">// filename
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> )
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// Ensure the artifact directory exists.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">:=&lt;/span> store.&lt;span style="color:#06287e">MkdirAll&lt;/span>(artifact); err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> ctrl.Result{}, err
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// Archive a directory into a tarball.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> &lt;span style="color:#60a0b0;font-style:italic">// The filter excludes .git and other VCS directories.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">:=&lt;/span> store.&lt;span style="color:#06287e">Archive&lt;/span>(&lt;span style="color:#666">&amp;amp;&lt;/span>artifact, &lt;span style="color:#4070a0">&amp;#34;/path/to/source/dir&amp;#34;&lt;/span>, &lt;span style="color:#007020;font-weight:bold">nil&lt;/span>); err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> ctrl.Result{}, err
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// At this point, artifact.Digest, artifact.Size, and
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> &lt;span style="color:#60a0b0;font-style:italic">// artifact.LastUpdateTime are automatically set by the SDK.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> ctrl.Result{}, &lt;span style="color:#007020;font-weight:bold">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="6-apply-the-externalartifact-status">6. Apply the ExternalArtifact status&lt;/h3>
&lt;p>After archiving, create or update the &lt;code>ExternalArtifact&lt;/code> resource in the cluster.
The &lt;code>ExternalArtifact&lt;/code> status must contain the artifact metadata so that
&lt;code>kustomize-controller&lt;/code> and &lt;code>helm-controller&lt;/code> can fetch and verify it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sourcev1 &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/source-controller/api/v1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metav1 &lt;span style="color:#4070a0">&amp;#34;k8s.io/apimachinery/pkg/apis/meta/v1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;sigs.k8s.io/controller-runtime/pkg/client&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">func&lt;/span> (r &lt;span style="color:#666">*&lt;/span>ArtifactGeneratorReconciler) &lt;span style="color:#06287e">reconcileExternalArtifact&lt;/span>(ctx context.Context,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name, namespace &lt;span style="color:#902000">string&lt;/span>, artifact meta.Artifact) &lt;span style="color:#902000">error&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ea &lt;span style="color:#666">:=&lt;/span> &lt;span style="color:#666">&amp;amp;&lt;/span>sourcev1.ExternalArtifact{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ObjectMeta: metav1.ObjectMeta{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Name: name,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Namespace: namespace,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> _, err &lt;span style="color:#666">:=&lt;/span> ctrl.&lt;span style="color:#06287e">CreateOrUpdate&lt;/span>(ctx, r.Client, ea, &lt;span style="color:#007020;font-weight:bold">func&lt;/span>() &lt;span style="color:#902000">error&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// Set the artifact status.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> ea.Status.Artifact = &lt;span style="color:#666">&amp;amp;&lt;/span>artifact
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// Mark the ExternalArtifact as ready.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> ea.Status.Conditions = []metav1.Condition{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Type: &lt;span style="color:#4070a0">&amp;#34;Ready&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Status: metav1.ConditionTrue,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> LastTransitionTime: metav1.&lt;span style="color:#06287e">Now&lt;/span>(),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Reason: &lt;span style="color:#4070a0">&amp;#34;Succeeded&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> Message: fmt.&lt;span style="color:#06287e">Sprintf&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;stored artifact for revision %s&amp;#34;&lt;/span>, artifact.Revision),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> })
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> err
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="7-implement-garbage-collection">7. Implement garbage collection&lt;/h3>
&lt;p>The SDK provides built-in garbage collection based on retention TTL and
record count limits:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;time&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4070a0">&amp;#34;github.com/fluxcd/pkg/artifact/storage&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020;font-weight:bold">func&lt;/span> (r &lt;span style="color:#666">*&lt;/span>ArtifactGeneratorReconciler) &lt;span style="color:#06287e">garbageCollect&lt;/span>(ctx context.Context, artifact meta.Artifact) &lt;span style="color:#902000">error&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// GarbageCollect removes stale artifacts based on the
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> &lt;span style="color:#60a0b0;font-style:italic">// configured retention TTL and max records.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> deleted, err &lt;span style="color:#666">:=&lt;/span> store.&lt;span style="color:#06287e">GarbageCollect&lt;/span>(ctx, artifact, &lt;span style="color:#40a070">5&lt;/span>&lt;span style="color:#666">*&lt;/span>time.Minute)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> err
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> &lt;span style="color:#007020">len&lt;/span>(deleted) &amp;gt; &lt;span style="color:#40a070">0&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#06287e">Info&lt;/span>(&lt;span style="color:#4070a0">&amp;#34;garbage collected artifacts&amp;#34;&lt;/span>, &lt;span style="color:#4070a0">&amp;#34;count&amp;#34;&lt;/span>, &lt;span style="color:#007020">len&lt;/span>(deleted))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">return&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="8-verify-artifact-integrity">8. Verify artifact integrity&lt;/h3>
&lt;p>At startup, verify that artifacts in storage have not been tampered with:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// Verify that the artifact on disk matches the expected digest.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>&lt;span style="color:#007020;font-weight:bold">if&lt;/span> err &lt;span style="color:#666">:=&lt;/span> store.&lt;span style="color:#06287e">VerifyArtifact&lt;/span>(artifact); err &lt;span style="color:#666">!=&lt;/span> &lt;span style="color:#007020;font-weight:bold">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> log.&lt;span style="color:#06287e">Error&lt;/span>(err, &lt;span style="color:#4070a0">&amp;#34;artifact integrity check failed&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// Re-fetch or re-generate the artifact.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="consuming-externalartifacts">Consuming ExternalArtifacts&lt;/h2>
&lt;p>Once your controller creates &lt;code>ExternalArtifact&lt;/code> resources, Flux users can
reference them in &lt;code>Kustomization&lt;/code> and &lt;code>HelmRelease&lt;/code> resources.&lt;/p>
&lt;h3 id="with-kustomization">With Kustomization&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kustomize.toolkit.fluxcd.io/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Kustomization&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>my-app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>apps&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">interval&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>10m&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sourceRef&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ExternalArtifact&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>my-app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">path&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;./&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">prune&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="with-helmrelease">With HelmRelease&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>helm.toolkit.fluxcd.io/v2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRelease&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>my-chart&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>apps&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">interval&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>10m&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">releaseName&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>my-chart&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">chartRef&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ExternalArtifact&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>my-chart&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="storage-api-reference">Storage API Reference&lt;/h2>
&lt;p>The &lt;code>storage.Storage&lt;/code> type provides the following operations:&lt;/p>
&lt;h3 id="artifact-creation">Artifact Creation&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Method&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>NewArtifactFor(kind, metadata, revision, fileName)&lt;/code>&lt;/td>
&lt;td>Create a new &lt;code>meta.Artifact&lt;/code> descriptor with URL&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>MkdirAll(artifact)&lt;/code>&lt;/td>
&lt;td>Create the artifact&amp;rsquo;s base directory&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Archive(artifact, dir, filter)&lt;/code>&lt;/td>
&lt;td>Archive a directory to a tarball with digest computation&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>AtomicWriteFile(artifact, reader, mode)&lt;/code>&lt;/td>
&lt;td>Atomically write content to the artifact path&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Copy(artifact, reader)&lt;/code>&lt;/td>
&lt;td>Atomically copy reader content to the artifact path&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>CopyFromPath(artifact, path)&lt;/code>&lt;/td>
&lt;td>Atomically copy a file to the artifact path&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="artifact-verification">Artifact Verification&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Method&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>ArtifactExist(artifact)&lt;/code>&lt;/td>
&lt;td>Check if an artifact exists in storage&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>VerifyArtifact(artifact)&lt;/code>&lt;/td>
&lt;td>Verify artifact integrity against its digest&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Lock(artifact)&lt;/code>&lt;/td>
&lt;td>Create a file lock for the artifact&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="artifact-cleanup">Artifact Cleanup&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Method&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>Remove(artifact)&lt;/code>&lt;/td>
&lt;td>Remove a single artifact file&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>RemoveAll(artifact)&lt;/code>&lt;/td>
&lt;td>Remove the artifact&amp;rsquo;s entire directory&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>RemoveAllButCurrent(artifact)&lt;/code>&lt;/td>
&lt;td>Remove all files except the current artifact&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>GarbageCollect(ctx, artifact, timeout)&lt;/code>&lt;/td>
&lt;td>GC stale artifacts based on retention policy&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="path-and-url-helpers">Path and URL Helpers&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Method&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>LocalPath(artifact)&lt;/code>&lt;/td>
&lt;td>Secure local path of an artifact (relative to &lt;code>BasePath&lt;/code>)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>SetArtifactURL(artifact)&lt;/code>&lt;/td>
&lt;td>Set the HTTP URL on an artifact&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>SetHostname(URL)&lt;/code>&lt;/td>
&lt;td>Replace the hostname of a URL&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Symlink(artifact, linkName)&lt;/code>&lt;/td>
&lt;td>Create or update a symlink for the artifact&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ArtifactPath(kind, ns, name, file)&lt;/code>&lt;/td>
&lt;td>Generate an artifact path string&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ArtifactDir(kind, ns, name)&lt;/code>&lt;/td>
&lt;td>Generate an artifact directory path string&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="security-best-practices">Security Best Practices&lt;/h2>
&lt;p>When building 3rd-party controllers that generate &lt;code>ExternalArtifact&lt;/code> resources,
follow these security guidelines from
&lt;a href="https://github.com/fluxcd/flux2/blob/main/rfcs/0012-external-artifact/README.md" target="_blank">RFC-0012&lt;/a>:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Authentication &amp;amp; Authorization&lt;/strong>: Use &lt;code>serviceAccountName&lt;/code> for workload
identity, &lt;code>secretRef&lt;/code> for long-lived credentials. Never cache credentials on
disk or in-memory.&lt;/li>
&lt;li>&lt;strong>TLS Encryption&lt;/strong>: Use &lt;code>certSecretRef&lt;/code> for custom CA certificates. Prefer
Mutual TLS authentication. Never skip TLS verification.&lt;/li>
&lt;li>&lt;strong>Provenance &amp;amp; Integrity&lt;/strong>: Verify upstream artifacts using Sigstore Cosign
or Notary Notation signatures. Prefer keyless verification with OIDC tokens.&lt;/li>
&lt;li>&lt;strong>Access Control&lt;/strong>: Expose a &lt;code>--no-cross-namespace-refs&lt;/code> flag to restrict
cross-namespace &lt;code>ExternalArtifact&lt;/code> generation. Use Kubernetes owner references
for garbage collection.&lt;/li>
&lt;li>&lt;strong>Least Privilege&lt;/strong>: Use a dedicated service account with minimal RBAC.
Conform with the restricted pod security standard (no root, read-only rootfs).&lt;/li>
&lt;li>&lt;strong>Storage Integrity&lt;/strong>: At startup, verify all stored artifact checksums
against the &lt;code>ExternalArtifact&lt;/code> digests in the cluster.&lt;/li>
&lt;li>&lt;strong>Network Policies&lt;/strong>: Restrict artifact endpoint access to only
&lt;code>kustomize-controller&lt;/code> and &lt;code>helm-controller&lt;/code>.&lt;/li>
&lt;/ul>
&lt;h2 id="policy-enforcement">Policy Enforcement&lt;/h2>
&lt;p>Cluster administrators can restrict which controllers can create
&lt;code>ExternalArtifact&lt;/code> resources using &lt;code>ValidatingAdmissionPolicy&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>admissionregistration.k8s.io/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ValidatingAdmissionPolicy&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;trusted-external-artifacts&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">failurePolicy&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Fail&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">matchConstraints&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">resourceRules&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">apiGroups&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>[&lt;span style="color:#4070a0">&amp;#34;source.toolkit.fluxcd.io&amp;#34;&lt;/span>]&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersions&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>[&lt;span style="color:#4070a0">&amp;#34;v1&amp;#34;&lt;/span>]&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">operations&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>[&lt;span style="color:#4070a0">&amp;#34;CREATE&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;UPDATE&amp;#34;&lt;/span>]&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">resources&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>[&lt;span style="color:#4070a0">&amp;#34;externalartifacts&amp;#34;&lt;/span>]&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">validations&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Restrict the artifacts to be served only by trusted endpoints&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">expression&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&amp;gt;&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> !has(object.status.artifact) ||
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> object.status.artifact.url.startsWith(&amp;#39;http://my-controller.flux-system.svc.cluster.local./&amp;#39;)&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Restrict the artifact operations to trusted service accounts&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">expression&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&amp;gt;&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> request.userInfo.username == &amp;#39;system:serviceaccount:flux-system:my-controller&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="further-reading">Further Reading&lt;/h2>
&lt;ul>
&lt;li>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/source/externalartifacts/">ExternalArtifact API Reference&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://github.com/fluxcd/flux2/blob/main/rfcs/0012-external-artifact/README.md" target="_blank">RFC-0012: External Artifact&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://pkg.go.dev/github.com/fluxcd/pkg/artifact" target="_blank">Artifact SDK Go Docs&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://github.com/fluxcd/source-watcher/tree/v2" target="_blank">Source Watcher Reference Implementation&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/gitops-toolkit/source-watcher/">Watching for source changes&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Flux: Advanced debugging</title><link>https://deploy-preview-2578--fluxcd.netlify.app/flux/gitops-toolkit/debugging/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2578--fluxcd.netlify.app/flux/gitops-toolkit/debugging/</guid><description>
&lt;p>This guide covers more advanced debugging topics such as collecting
runtime profiling data from GitOps Toolkit components.&lt;/p>
&lt;p>As a user, this page normally should be a last resort, but you may
be asked by a maintainer to share a
&lt;a href="#collecting-a-profile">collected profile&lt;/a>
to debug e.g. performance issues.&lt;/p>
&lt;h2 id="pprof">Pprof&lt;/h2>
&lt;p>The
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/components/">GitOps Toolkit components&lt;/a> serve
&lt;a href="https://golang.org/pkg/net/http/pprof/" target="_blank">&lt;code>pprof&lt;/code>&lt;/a>
runtime profiling data on their metrics HTTP server (default &lt;code>:8080&lt;/code>).&lt;/p>
&lt;h3 id="endpoints">Endpoints&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Endpoint&lt;/th>
&lt;th>Path&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Index&lt;/td>
&lt;td>&lt;code>/debug/pprof/&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CPU profile&lt;/td>
&lt;td>&lt;code>/debug/pprof/profile&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Symbol&lt;/td>
&lt;td>&lt;code>/debug/pprof/symbol&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Trace&lt;/td>
&lt;td>&lt;code>/debug/pprof/trace&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="collecting-a-profile">Collecting a profile&lt;/h3>
&lt;p>To collect a profile, port-forward to the component&amp;rsquo;s metrics endpoint
and collect the data from the
&lt;a href="#endpoints">endpoint&lt;/a> of choice:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>$ kubectl port-forward -n &amp;lt;namespace&amp;gt; deploy/&amp;lt;component&amp;gt; &lt;span style="color:#40a070">8080&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>$ curl -Sk -v http://localhost:8080/debug/pprof/heap &amp;gt; heap.out
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The collected profile
&lt;a href="https://blog.golang.org/pprof" target="_blank">can be analyzed using &lt;code>go&lt;/code>&lt;/a>,
or shared with one of the maintainers.&lt;/p>
&lt;h2 id="resource-usage">Resource usage&lt;/h2>
&lt;p>As &lt;code>kubectl top&lt;/code> gives a limited (and at times inaccurate) overview of
resource usage, it is often better to make use of the Grafana metrics
to gather insights. See
&lt;a href="https://deploy-preview-2578--fluxcd.netlify.app/flux/monitoring/metrics/">Flux Prometheus metrics&lt;/a> for a
guide on how to visualize this data with a Grafana dashboard.&lt;/p></description></item></channel></rss>