<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Armory Docs – User Guides</title><link>/continuous-deployment/spinnaker-user-guides/</link><description>Recent content in User Guides on Armory Docs</description><generator>Hugo -- gohugo.io</generator><atom:link href="/continuous-deployment/spinnaker-user-guides/index.xml" rel="self" type="application/rss+xml"/><item><title>Continuous-Deployment: Spinnaker's Application Screen</title><link>/continuous-deployment/spinnaker-user-guides/application-screen/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/application-screen/</guid><description>
&lt;p>An application within Spinnaker&lt;sup>TM&lt;/sup> is a combination of clusters and load balancers.&lt;/p>
&lt;p>To get to the application details screen, select the &amp;lsquo;Applications&amp;rsquo; tab on the top navigation bar in Spinnaker. Then pick an application from the list. It should load to a screen that looks like:&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-04-03-at-12.49.10-PM.png"/>
&lt;/figure>
&lt;p>This is a view of all of the server groups (which make up your cluster), grouped by stack. In the image above, I have three server groups. Two server groups are in the &amp;lsquo;hellodeploy-cron&amp;rsquo; stack, namely V948 and V947. Notice that V947 is greyed out and doesn&amp;rsquo;t contain any instances. In other words, it is disabled and scaled down to zero. The last server group, V013, is in the &amp;lsquo;hellodeploy-preprod&amp;rsquo; stack. It has one instance.&lt;/p>
&lt;p>From here, we can click on different things:&lt;/p>
&lt;h2 id="cluster-tab">Cluster tab&lt;/h2>
&lt;p>Active Server group selected:&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-03-30-at-5.48.23-PM.png"/>
&lt;/figure>
&lt;p>Disabled server group selected:&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-03-30-at-5.50.26-PM.png"/>
&lt;/figure>
&lt;p>Instance selected:&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-03-30-at-5.49.21-PM.png"/>
&lt;/figure>
&lt;p>Load balancer selected:&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-03-30-at-5.49.37-PM.png"/>
&lt;/figure>
&lt;h2 id="load-balancer-tab">Load balancer tab&lt;/h2>
&lt;p>Load balancer selected:&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-03-30-at-5.51.44-PM.png"/>
&lt;/figure>
&lt;h2 id="pipelines">Pipelines&lt;/h2>
&lt;p>If you select the &amp;lsquo;Pipelines&amp;rsquo; tab, you will see something like:&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-04-03-at-12.57.39-PM.png"/>
&lt;/figure>
&lt;p>This page shows two pipelines, &amp;lsquo;Deploy&amp;rsquo; and &amp;lsquo;Cron Deploy&amp;rsquo;. The &amp;lsquo;Deploy&amp;rsquo; pipeline shows two executions, both labeled &amp;lsquo;BUILD #5&amp;rsquo;, this indicates that they were triggered by Jenkins&amp;rsquo; build #5. Clicking on the build number will take you to the Jenkins job. The red block on the second execution indicates that it has failed. If I click on the red part of the execution, it will expand into:&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-04-03-at-1.05.01-PM.png"/>
&lt;/figure>
&lt;p>Now I can see that the execution failed because of a subnet issue.&lt;/p>
&lt;p>For more information about pipelines, check out the &lt;a href="/plugins/policy-engine/use/packages/spinnaker.http.authz/pipelines/">pipeline guide&lt;/a>.&lt;/p>
&lt;h2 id="deleting-an-application">Deleting an application&lt;/h2>
&lt;p>To delete an application, first click on the &amp;lsquo;Applications&amp;rsquo; tab on Spinnaker&amp;rsquo;s top navigation bar. Select the application you would like to delete and enter its application detail page. Now select the &amp;lsquo;Config&amp;rsquo; tab. Scroll all the way down to the &amp;lsquo;Delete Application&amp;rsquo; section.&lt;/p>
&lt;p>Example:
&lt;figure>
&lt;img src="/images/Image-2017-04-03-at-1.09.20-PM.png"/>
&lt;/figure>
&lt;/p>
&lt;p>You can see that in order to delete the application, you first need to delete its server groups. To delete its server groups, select the &amp;lsquo;Clusters&amp;rsquo; tab. Now select a server group to delete by clicking on it.&lt;/p>
&lt;p>Example of V952 selected:
&lt;figure>
&lt;img src="/images/Image-2017-04-03-at-1.10.34-PM.png"/>
&lt;/figure>
&lt;/p>
&lt;p>Now press the &amp;lsquo;Server Group Actions&amp;rsquo; from the bar on the right hand side and press &amp;lsquo;Destroy&amp;rsquo;. Spinnaker asks you to confirm that you are going to delete this server group.&lt;/p>
&lt;p>Once all of the server groups have been deleted, navigate back to the &amp;lsquo;Config&amp;rsquo;-&amp;gt;&amp;lsquo;Delete Application&amp;rsquo; section and press &amp;ldquo;Delete Application&amp;rsquo;.&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-04-03-at-1.15.56-PM.png"/>
&lt;/figure></description></item><item><title>Continuous-Deployment: Artifact Progression Through Environments in Spinnaker</title><link>/continuous-deployment/spinnaker-user-guides/artifact-promotion/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/artifact-promotion/</guid><description>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>&lt;a href="https://www.spinnaker.io/guides/tutorials/codelabs/kubernetes-v2-source-to-prod/#allow-github-to-post-push-events">Configure Github to post push events&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://www.spinnaker.io/guides/tutorials/codelabs/kubernetes-v2-source-to-prod/#configure-kubernetes">Add two Kubernetes accounts (staging and prod)&lt;/a>&lt;/p>
&lt;h2 id="overview-of-promoting-artifacts-between-environments">Overview of promoting artifacts between environments&lt;/h2>
&lt;p>One of the more powerful and common uses of Spinnaker is the promotion of
an artifact through a series of environments, i.e. from &lt;em>staging&lt;/em> to &lt;em>prod&lt;/em>.&lt;/p>
&lt;p>In such a workflow, a pipeline might initially be triggered by the creation or
modification of an artifact. While the artifact itself may be a manifest, a
Docker image, or a number of other possibilities, what is important is that
once it is initially deployed, the artifact&amp;rsquo;s subsequent promotion is
dependent on the result of the artifact&amp;rsquo;s deployment to the previous
environment.&lt;/p>
&lt;p>Determining promotion success can be accomplished through the completion of
integration tests or another automated method but it can also be determined
through manual judgement. We make use of the latter case in our example below.&lt;/p>
&lt;p>Below is an example where we deploy an artifact to our &lt;em>staging&lt;/em> environment.
The successful completion of this pipeline then triggers another pipeline
configured with a &lt;code>Manual Judgement&lt;/code> stage. If &lt;em>manual judgement&lt;/em> succeeds, a
third stage is triggered which takes the same artifact we initially deployed
into our &lt;em>staging&lt;/em> environment and deploys it to our &lt;em>production&lt;/em> environment.&lt;/p>
&lt;h3 id="deploy-to-staging">Deploy to Staging&lt;/h3>
&lt;p>We begin by creating a new application named &lt;code>artifact-promotion-demo&lt;/code>. Once
you have created your application, click on &lt;code>Pipelines&lt;/code> and then &lt;code>Configure a new pipeline&lt;/code>.&lt;/p>
&lt;p>We want this initial pipeline to be responsible for our artifact&amp;rsquo;s deployment
to &lt;em>staging&lt;/em>. With this in mind, we have named it &lt;code>Deploy to Staging&lt;/code>&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2018-10-23-at-2.03.33-PM.png"/>
&lt;/figure>
&lt;p>You should now configure the expected artifacts for your pipeline and specify
how your pipeline should be triggered. In our example we have specified a
manifest (stored in our Github repo) as our artifact and set our pipeline to
trigger automatically when changes to our manifest are detected.&lt;/p>
&lt;figure>
&lt;img src="/images/artifacts_and_triggers.png"/>
&lt;/figure>
&lt;p>Next, click &lt;code>Add stage&lt;/code>. Provide your new stage a descriptive name &amp;ndash; we have
chosen &lt;code>Deploy to Staging&lt;/code> as ours. In order to deply our artifact, we choose
&lt;code>Deploy (Manifest)&lt;/code> as our &lt;em>Stage Type&lt;/em> and specify &lt;code>Artifact&lt;/code> as our &lt;em>Manifest
Source&lt;/em>. Finally, we select our arfifact from the &lt;code>Expected Artifact&lt;/code> dropdown.&lt;/p>
&lt;figure>
&lt;img src="/images/artifacts_deploy_manifest.png"/>
&lt;/figure>
&lt;p>Save your changes.&lt;/p>
&lt;h3 id="validate-your-deployment">Validate your Deployment&lt;/h3>
&lt;p>Now that we have a pipeline deploying to staging, we want validate that the
deployment was successful before promoting our artifact to production.&lt;/p>
&lt;p>Click &lt;code>Pipelines&lt;/code> and then click &lt;code>+ Create&lt;/code> to create a new pipeline. We have
named ours &lt;code>Staging Judgement&lt;/code>.&lt;/p>
&lt;p>Add an automated trigger to the pipeline where the type is &lt;code>Pipeline&lt;/code>. Select
the &lt;code>Deploy to Staging&lt;/code> pipeline from the &lt;code>Pipeline&lt;/code> dropdown. Check both
&lt;code>successful&lt;/code> and &lt;code>Trigger Enabled&lt;/code> from the remaining options.&lt;/p>
&lt;figure>
&lt;img src="/images/artifacts_automated_triggers.png"/>
&lt;/figure>
&lt;p>Now click, &lt;code>Add stage&lt;/code> and select &lt;code>Manual Judgement&lt;/code> as the &lt;em>Stage Type&lt;/em>. In
&lt;em>Execution Options&lt;/em> select &lt;code>halt the entire pipeline&lt;/code> for &lt;em>If stage fails&lt;/em>.
Additionally, you can provide additional instructions in the &lt;em>Instructions&lt;/em>
text-area.&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2018-10-23-at-1.47.21-PM.png"/>
&lt;/figure>
&lt;p>Save your changes.&lt;/p>
&lt;h3 id="deploy-to-production">Deploy to Production&lt;/h3>
&lt;p>We are now ready to create the pipeline responsible for deploying
our artifact to &lt;em>production&lt;/em>.&lt;/p>
&lt;p>Click &lt;code>Pipelines&lt;/code> and then click &lt;code>+ Create&lt;/code> to create a new pipeline. We have
named this one &lt;code>Deploy to Production&lt;/code> and have made use of the &lt;code>Copy From&lt;/code>
dropdown in order to copy our &lt;code>Deploy to Staging&lt;/code> pipeline.&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2018-10-23-at-1.48.19-PM.png"/>
&lt;/figure>
&lt;p>You will want to delete the &lt;em>Automated Trigger&lt;/em> copied from our &lt;code>Deploy to Staging&lt;/code> pipeline. In its place, create a new one of &lt;em>Type&lt;/em> &lt;code>Pipeline&lt;/code>. In
the &lt;em>Pipeline&lt;/em> dropdown select &lt;code>Staging Judgement&lt;/code>. Lastly, select
&lt;code>successful&lt;/code> for &lt;em>Pipeline Status&lt;/em> and select &lt;code>Trigger Enabled&lt;/code>&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2018-10-23-at-1.50.56-PM.png"/>
&lt;/figure>
&lt;p>Finally, add a new stage of type &lt;code>Deploy (Manifest)&lt;/code>. The configuration for this
stage should mirror the configuration of the &lt;code>Deploy (Manifest)&lt;/code> stage of
the &lt;em>Deploy to Staging&lt;/em> pipeline with the exception that specified account
should correspond to your production environment.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>We have just shown an example of promoting a single artifact through a
series of environments. While we relied on a &lt;em>Manual Judgement&lt;/em> stage in order to
validate the deployment of our artifact to &lt;em>staging&lt;/em> before deploying it to
&lt;em>production&lt;/em>, we could also have used automated methods such as the
successful completion of integration tests or through the use of canary analysis.
Finally, although the example relied on only two distinct environments, adding
additional environments is trivial.&lt;/p>
&lt;h2 id="see-also">See Also&lt;/h2>
&lt;p>Here are some other resources that may provide additional insight into the
promotion of artifacts through a series of environments:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;a href="https://www.spinnaker.io/guides/tutorials/codelabs/kubernetes-v2-source-to-prod/">Spinnaker Codelab: Kubernetes Source To Prod (Manifest Based)&lt;/a> &amp;ndash; Steps 6 and 7 are of particular interest.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Watch this video about &lt;a href="https://youtu.be/u7QF2X4WzE8?t=360">Deploying Helm Charts with Armory&lt;/a> &amp;ndash; Example of artifact promotion through environments managed by a single pipeline.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Github Repo example - &lt;a href="https://github.com/armory/spin-helm-demo">https://github.com/armory/spin-helm-demo&lt;/a>&lt;/p>
&lt;/li>
&lt;/ul></description></item><item><title>Continuous-Deployment: Automated Kubernetes Rollbacks in Spinnaker</title><link>/continuous-deployment/spinnaker-user-guides/automated-rollbacks/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/automated-rollbacks/</guid><description>
&lt;h2 id="creating-a-main-deployment-pipeline">Creating a main deployment pipeline&lt;/h2>
&lt;p>Let’s start by creating a new pipeline that will deploy a simple nginx container:&lt;/p>
&lt;figure>
&lt;img src="/images/aut-rollbacks-deploy-pipeline.png"/>
&lt;/figure>
&lt;p>We’ll be supplying the manifest directly in the Deploy (Manifest) stage as text for simplicity, but it can also be taken from a version control system like Github:&lt;/p>
&lt;figure>
&lt;img src="/images/aut-rollbacks-deploy-manifest-1.png"/>
&lt;/figure>
&lt;p>This is the sample deployment manifest, which will create two nginx pods in Kubernetes under the “german-env1” namespace (the namespace must exist already):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-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:#ff79c6">apiVersion&lt;/span>: apps/v1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">kind&lt;/span>: Deployment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">name&lt;/span>: nginx-deployment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">namespace&lt;/span>: german-env1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">selector&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">matchLabels&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">app&lt;/span>: nginx
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">replicas&lt;/span>: &lt;span style="color:#bd93f9">2&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">template&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">labels&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">app&lt;/span>: nginx
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">containers&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ff79c6">name&lt;/span>: nginx
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">image&lt;/span>: nginx:1.14.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">ports&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ff79c6">containerPort&lt;/span>: &lt;span style="color:#bd93f9">80&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is all that is required in the deploy pipeline. We can now save and execute manually the pipeline and it should be successful.&lt;/p>
&lt;h2 id="generating-historic-deployment-versions-for-testing-rollbacks">Generating historic deployment versions for testing rollbacks&lt;/h2>
&lt;p>We’ll test rollbacks by making a change to the deployment manifest and running the pipeline again, so that we’ll end up with two historic versions of the deployment. First we’ll edit the manifest to change nginx image version from &lt;code>nginx:1.14.0&lt;/code> to &lt;code>nginx:1.15.05-alpine&lt;/code> and then run the pipeline. Now under infrastructure view we’ll see that we have two historic versions of the deployment:&lt;/p>
&lt;figure>
&lt;img src="/images/aut-rollbacks-infra-1.png"/>
&lt;/figure>
&lt;p>The first version deployed (&lt;code>V001&lt;/code>) corresponds to nginx 1.14.0, while the second version (&lt;code>V002&lt;/code>) corresponds to nginx 1.15.5-alpine.&lt;/p>
&lt;h2 id="creating-a-rollback-pipeline">Creating a rollback pipeline&lt;/h2>
&lt;p>Now we’ll create a rollback pipeline that triggers automatically when &lt;code>deploy-nginx&lt;/code> pipeline fails:&lt;/p>
&lt;figure>
&lt;img src="/images/aut-rollbacks-rollpipeline-1.png"/>
&lt;/figure>
&lt;figure>
&lt;img src="/images/aut-rollbacks-trigger.png"/>
&lt;/figure>
&lt;p>This pipeline just contains a single stage of type Undo Rollout (Manifest) with the following configuration:&lt;/p>
&lt;figure>
&lt;img src="/images/aut-rollbacks-undo-rollout-1.png"/>
&lt;/figure>
&lt;p>This configuration is important, the fields &lt;code>Namespace&lt;/code>, &lt;code>Kind&lt;/code> and &lt;code>Name&lt;/code> are the coordinates of the Kubernetes deployment, and should match what is defined in the deployment manifest on the deploy pipeline. &lt;code>Revisions Back&lt;/code> indicate how many deployment versions to rollback. In our historic running we currently have &lt;code>V001&lt;/code> and &lt;code>V002&lt;/code>. Each time a deployment occurs, this version is automatically increased. &lt;a href="https://www.spinnaker.io/guides/user/kubernetes-v2/automated-rollbacks/#automated-rollbacks">Click here for detailed information on how Undo Rollout (Manifest) stage works&lt;/a>.&lt;/p>
&lt;h2 id="testing-rollback-logic">Testing rollback logic&lt;/h2>
&lt;p>Now we’ll do a rollback test by changing the manifest, providing an invalid value for the namespace to force a failure. In deploy-nginx we’ll change the namespace to an invalid value:&lt;/p>
&lt;figure>
&lt;img src="/images/aut-rollbacks-deploy-manifest-invalid-1.png"/>
&lt;/figure>
&lt;p>We’ll run the pipeline again to see the rollback pipeline kick in automatically to rollback one version of the deployment:&lt;/p>
&lt;figure>
&lt;img src="/images/aut-rollbacks-exec-history-1.png"/>
&lt;/figure>
&lt;p>If we go to the infrastructure view we can see that the active deployment was rolled back to the previous version:&lt;/p>
&lt;figure>
&lt;img src="/images/aut-rollbacks-infra-2.png"/>
&lt;/figure>
&lt;p>In this case, &lt;code>V003&lt;/code> was generated by the rollback operation with the contents of a previous deployment. This is the summary of events:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;strong>Versions before failed deployment&lt;/strong>&lt;/th>
&lt;th>&lt;strong>Versions after failed deployment&lt;/strong>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>V001 (nginx:1.14.0)&lt;/td>
&lt;td>V002 (nginx:1.15.5-alpine)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>V002 (nginx:1.15.5-alpine) → ACTIVE&lt;/td>
&lt;td>V003 (nginx:1.14.0) → ACTIVE&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Here we can see one gotcha of the Undo Rollout (Manifest) stage: &lt;em>&lt;strong>it will rollback a version no matter if the failed pipeline deployed the application or not&lt;/strong>&lt;/em>. So make sure that this stage only executes after a successful deployment of the artifact to rollback, and then some other logic marks the pipeline as failed, otherwise this may have the unintended consequence of rolling back a healthy version. One use case of this may be a pipeline that first deploys some configuration changes to Kubernetes, then deploys an updated version of a service. We can have a rollback pipeline execute to rollback the config changes if the main service deployment fails.&lt;/p>
&lt;p>What happens if we don&amp;rsquo;t have a rollback pipeline? In the previous case where the namespace is invalid nothing is deployed and we can end up with a clean state, but if something goes wrong after a deployment we&amp;rsquo;ll end up with a failed server group. To see this in action let&amp;rsquo;s change nginx deployment to a non-existent version (1000), disable the rollback pipeline and see how ends up the infrastructure view after a failed deployment:&lt;/p>
&lt;figure>
&lt;img src="/images/aut-rollbacks-infra-3.png"/>
&lt;/figure>
&lt;p>This is clearly not what we want, although the previous version (V003 - nginx:1.14.0) is still active and running, we have a failed server group (replica set) that cannot be started, so the rollback pipeline is still necessary to clean this up. In the next section we’ll look at a more advanced way for detecting when to apply a rollback.&lt;/p>
&lt;h2 id="advanced-configuration-protecting-against-unwanted-rollbacks">Advanced configuration: Protecting against unwanted rollbacks&lt;/h2>
&lt;p>A deployment pipeline can fail because of many different reasons, but a rollback needs to be executed only in case something broken was deployed. One way to detect this is by using conditional checks with Spring Expressions to detect the case where something broken was deployed. In the rollback pipeline we’ll add two more stages of type “Check Preconditions” before the Undo Rollout stage:&lt;/p>
&lt;figure>
&lt;img src="/images/aut-rollbacks-rollpipeline-2.png"/>
&lt;/figure>
&lt;p>This allow us to write expressions that continue the pipeline only if a broken deployment occurred. Every pipeline execution has a “context” object holding information about all aspects of execution. We can see this information clicking the “Source” link inside the details of a previous execution:&lt;/p>
&lt;figure>
&lt;img src="/images/aut-rollbacks-exec-details-1.png"/>
&lt;/figure>
&lt;p>This opens a new tab showing a json similar to this:&lt;/p>
&lt;figure>
&lt;img src="/images/aut-rollbacks-exec-json-1.png"/>
&lt;/figure>
&lt;p>We’re looking for two pieces on information inside this json:&lt;/p>
&lt;ul>
&lt;li>A stage of type &lt;code>deployManifest&lt;/code> in the trigger pipeline execution that failed.&lt;/li>
&lt;li>This stage should have created an artifact.&lt;/li>
&lt;/ul>
&lt;p>The corresponding Spring Expression for the “Deploy stage failed?” stage is the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-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>${ trigger[&amp;#39;parentExecution&amp;#39;][&amp;#39;stages&amp;#39;].?[type == &amp;#39;deployManifest&amp;#39;][0][&amp;#39;status&amp;#39;].toString() == &amp;#39;TERMINAL&amp;#39; }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And for the “Artifact created?” stage:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-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>${ trigger[&amp;#39;parentExecution&amp;#39;][&amp;#39;stages&amp;#39;].?[type == &amp;#39;deployManifest&amp;#39;][0][&amp;#39;outputs&amp;#39;][&amp;#39;outputs.createdArtifacts&amp;#39;] != null }
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In these expressions we are looking into the trigger pipeline execution for the first stage of type &lt;code>deployManifest&lt;/code> whose status is &lt;code>TERMINAL&lt;/code> (failed stage) AND that created some artifacts. &lt;a href="https://www.spinnaker.io/reference/pipeline/expressions/">Here’s the full reference documentation for pipeline expressions&lt;/a>.&lt;/p>
&lt;figure>
&lt;img src="/images/aut-rollbacks-precond-1.png"/>
&lt;/figure>
&lt;p>Also we make sure to uncheck “Fail Pipeline”, so that the pipeline doesn’t fail when there’s nothing to rollback, just stops execution. Using Spring Expressions we can also specify the coordinates of the artifact to rollback in Undo Rollout stage, taking them from the above &lt;code>outputs.createdArtifacts&lt;/code> object:&lt;/p>
&lt;p>&lt;strong>Namespace&lt;/strong>: &lt;code>${ trigger['parentExecution']['stages'].?[type == 'deployManifest'][0]['outputs']['outputs.createdArtifacts'][0]['location'] }&lt;/code>&lt;/p>
&lt;p>&lt;strong>Name&lt;/strong>: &lt;code>${ trigger['parentExecution']['stages'].?[type == 'deployManifest'][0]['outputs']['outputs.createdArtifacts'][0]['name'] }&lt;/code>&lt;/p>
&lt;p>&lt;strong>Kind&lt;/strong>: &lt;code>${ trigger['parentExecution']['stages'].?[type == 'deployManifest'][0]['outputs']['outputs.createdArtifacts'][0]['type'].substring('kubernetes/'.length()) }&lt;/code>&lt;/p>
&lt;p>This allow us to have an Undo Rollout stage a bit more resilient in case the artifact coordinates change:&lt;/p>
&lt;figure>
&lt;img src="/images/aut-rollbacks-undo-rollout-2.png"/>
&lt;/figure>
&lt;p>Now we can repeat the tests and see that the rollback only executes when changing nginx version to a non existent version, but it doesn’t execute if the namespace is invalid, even though both scenarios fail the deployment pipeline.&lt;/p></description></item><item><title>Continuous-Deployment: Automating Spinnaker</title><link>/continuous-deployment/spinnaker-user-guides/writing-scripts/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/writing-scripts/</guid><description>
&lt;h2 id="api-docs">API docs&lt;/h2>
&lt;p>People often ask how they can write scripts and use Spinnaker™ programmatically. Spinnaker is a collection of subservices that all expose a RESTful API. You can see a list (with descriptions) of all of the endpoints by navigating to:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-fallback" data-lang="fallback">&lt;span style="display:flex;">&lt;span>http(s)://&amp;lt;your-gate-url&amp;gt;/swagger-ui.html
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note: you may need to append your url with the gate port, &lt;code>:8084&lt;/code>.&lt;/p>
&lt;p>You should see a screen that looks like:&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-04-03-at-4.06.51-PM.png"/>
&lt;/figure>
&lt;p>You can click on the controller you are interested in to see endpoints related to it. You can even test out hitting these different endpoints right in the UI.&lt;/p>
&lt;h2 id="auth">Auth&lt;/h2>
&lt;p>Being able to access the API when auth is enabled requires a certain configuration by your Spinnaker Administrator. They will need to enable programmatic access via mutual tls (x509 certs). Then you will need to use a cert when communicating with the API.&lt;/p></description></item><item><title>Continuous-Deployment: AWS Guides for Spinnaker</title><link>/continuous-deployment/spinnaker-user-guides/aws-guides/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/aws-guides/</guid><description/></item><item><title>Continuous-Deployment: Azure Guides for Armory CD and Spinnaker</title><link>/continuous-deployment/spinnaker-user-guides/azure-guides/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/azure-guides/</guid><description/></item><item><title>Continuous-Deployment: Canary Analysis in Spinnaker</title><link>/continuous-deployment/spinnaker-user-guides/canary/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/canary/</guid><description/></item><item><title>Continuous-Deployment: Cloud Foundry Best Practices</title><link>/continuous-deployment/spinnaker-user-guides/best-practices-cf/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/best-practices-cf/</guid><description>
&lt;h2 id="buildpacks-and-procfiles">Buildpacks and Procfiles&lt;/h2>
&lt;p>Buildpacks typically define processes that run with some default values. These default values include things like starting process and commands. Some buildpacks, like Python, do not specify any default processes. During staging, the following error occurs if there are no processes defined: &lt;code>StagingError - Staging error: No process types returned from stager.&lt;/code> Any buildpacks that do not specify a default process run into the same issue.&lt;/p>
&lt;p>When developers use the &lt;code>COMMAND&lt;/code> option in a manifest with the Cloud Foundry (CF) CLI, this command gets propagated to the process, and it gets used as expected. This is, in part, due to the fact that the CF CLI uses the v2 endpoints for most of the operations. Spinnaker, however, is configured to use various v3 endpoints. These allow more granular control over the deployment.&lt;/p>
&lt;p>The solution is to use &lt;a href="https://docs.cloudfoundry.org/buildpacks/prod-server.html#procfile">Procfiles&lt;/a> if the buildpack does not include any default processes, like the Python buildpack. These Procfiles live at the root of an application and are evaluated during staging. This ensures that the staging does not fail because no processes are present.&lt;/p>
&lt;p>Once you have a Procfile, additional customization can be added by using the &lt;code>COMMAND&lt;/code> option in manifests. Any options supplied this way override the Procfile. But, start with a Procfile to ensure that stages do not fail because of missing commands.&lt;/p></description></item><item><title>Continuous-Deployment: Debian Packages</title><link>/continuous-deployment/spinnaker-user-guides/debian-packages/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/debian-packages/</guid><description>
&lt;h2 id="why-use-debian-packages">Why use Debian packages&lt;/h2>
&lt;p>While Spinnaker is flexible enough to use any dependency management system, it is predisposed to manage Debian packages due to its default settings with Rosco, Orca, and Jenkins.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Out-of-the-box settings for Spinnaker look for an archived package with a &lt;code>.deb&lt;/code> suffix within Jenkins. Spinnaker also gets the version from the package and automatically appends it to the package name within Rosco. This makes it easy to specify your package in Rosco without the version number, &lt;code>mycompany-app&lt;/code>. However, during the bake provisioning process Spinnaker installs the version that was specified by the Jenkins build: &lt;code>mycompany-app.3.24.9-3&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Debian packaging allows service teams to easily add their app specific configuration to common Packer templates. If you&amp;rsquo;re using any Debian-based system (Ubuntu, DSL, Astra, etc), you&amp;rsquo;ll likely be using Debian packages for your system configuration and dependency management. So it&amp;rsquo;s a natural extension to use a Debian package for your own applications. Using Debian packages helps reduce the variations in Packer templates, or variables passed to Packer templates, during the bake process.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="creating-debian-packages">Creating Debian packages&lt;/h2>
&lt;p>You can create a Debian package by using various open source packaging tools. If you&amp;rsquo;re using Java, use the &lt;code>OS Package&lt;/code> library. You can also use the &lt;a href="https://www.debian.org/doc/manuals/maint-guide/build.en.html">packaging tools provided by Debian&lt;/a>.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Language&lt;/th>
&lt;th>Tool&lt;/th>
&lt;th>Package Types&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Java&lt;/td>
&lt;td>&lt;a href="https://github.com/nebula-plugins/gradle-ospackage-plugin">OS Package&lt;/a>&lt;/td>
&lt;td>deb/rpm&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Python&lt;/td>
&lt;td>&lt;a href="https://pypi.python.org/pypi/stdeb/0.8.5">stdeb&lt;/a>&lt;/td>
&lt;td>deb&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Node&lt;/td>
&lt;td>&lt;a href="https://www.npmjs.com/package/node-deb">node-deb&lt;/a>&lt;/td>
&lt;td>deb&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PHP&lt;/td>
&lt;td>&lt;a href="https://github.com/wdalmut/php-deb-packager">php-deb-packager&lt;/a>&lt;/td>
&lt;td>deb&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Any&lt;/td>
&lt;td>&lt;a href="https://github.com/crohr/pkgr">pkgr&lt;/a>&lt;/td>
&lt;td>deb/rpm&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Any&lt;/td>
&lt;td>&lt;a href="https://github.com/jordansissel/fpm/wiki">fpm&lt;/a>&lt;/td>
&lt;td>deb/rpm/others&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="example-debian-package-with-ospackage-gradle-plugin">Example: Debian package with OSPackage Gradle plugin&lt;/h3>
&lt;p>Begin by creating a &lt;code>build.gradle&lt;/code>. You also need to create a &lt;code>config/scripts/post-install.sh&lt;/code> file in your project directory.&lt;/p>
&lt;p>Below is an example of what a Gradle file might look like for an app that builds a &lt;code>war&lt;/code>. This uses the &lt;a href="https://github.com/nebula-plugins/gradle-ospackage-plugin">gradle-ospackage-plugin&lt;/a> package. Basic usage of the Deb Plugin in the &lt;a href="https://github.com/nebula-plugins/gradle-ospackage-plugin/wiki/Deb-Plugin">Deb Plugin docs&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>buildscript {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> repositories {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> jcenter()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> maven { url &lt;span style="color:#f1fa8c">&amp;#34;https://plugins.gradle.org/m2/&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> dependencies {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> classpath &lt;span style="color:#f1fa8c">&amp;#39;com.netflix.nebula:gradle-ospackage-plugin:8.5.6&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>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>apply plugin&lt;span style="color:#ff79c6">:&lt;/span> &lt;span style="color:#f1fa8c">&amp;#39;nebula.ospackage&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>ospackage {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> packageName &lt;span style="color:#ff79c6">=&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;mycompanyname-service&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> version &lt;span style="color:#ff79c6">=&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;1.10.3&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> requires(&lt;span style="color:#f1fa8c">&amp;#39;nginx&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> postInstall file(&lt;span style="color:#f1fa8c">&amp;#39;config/scripts/post-install.sh&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> from(&lt;span style="color:#f1fa8c">&amp;#39;build/application.war&amp;#39;&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> into &lt;span style="color:#f1fa8c">&amp;#39;/opt/application/&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>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then build your Debian package based on your Gradle build file:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>gradle buildDeb
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If the build succeeds then you should find a Debian package in the following path:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>./build/distributions/mycompanyname-service.1.10.3_all.deb
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Continuous-Deployment: Deploy a Docker Image to Kubernetes</title><link>/continuous-deployment/spinnaker-user-guides/kubernetes-deployments/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/kubernetes-deployments/</guid><description>
&lt;h2 id="kubernetes-deployments">Kubernetes Deployments&lt;/h2>
&lt;p>Spinnaker delegates the deployment of containers to Kubernetes. Kubernetes then proceeds with a rolling update deployment which is effectively a rolling blue/green deployment.
Pods are created in batches and when a pod is deemed &lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/">healthy&lt;/a> it starts receiving traffic.&lt;/p>
&lt;h3 id="example">Example&lt;/h3>
&lt;p>This example focuses on the manifest-based &lt;a href="/continuous-deployment/spinnaker-user-guides/kubernetes-v2/">Kubernetes V2 provider&lt;/a>.&lt;/p>
&lt;p>We&amp;rsquo;ve defined a simple pipeline where we first define an artifact (a Docker image) with a version coming from a pipeline parameter.
You&amp;rsquo;d usually get the image from a trigger via container registry or a Jenkins job with the same result.&lt;/p>
&lt;figure>
&lt;img src="/images/kubernetes_deployments_1.png"/>
&lt;/figure>
&lt;p>The second step defines the deployment of that image. It&amp;rsquo;s a simple &amp;ldquo;&lt;em>Deploy (Manifest)&lt;/em>&amp;rdquo; stage. Here we&amp;rsquo;re adding the static deployment manifest via a text field but you&amp;rsquo;d usually retrieve it as an artifact directly
or via a Helm bake stage.&lt;/p>
&lt;div class="alert alert-primary" role="alert">
&lt;h4 class="alert-heading">Note&lt;/h4>
For more information, watch this video about &lt;a href="https://youtu.be/u7QF2X4WzE8?t=360">Deploying Helm Charts with Armory&lt;/a> – Example of artifact promotion through environments managed by a single pipeline.
&lt;/div>
&lt;figure>
&lt;img src="/images/kubernetes_deployments_2.png"/>
&lt;/figure>
&lt;p>As a matter of fact, the deployment manifest is not entirely static: Spinnaker will replace the image name with the actual tagged name from the bound artifact.&lt;/p>
&lt;p>Let&amp;rsquo;s see how a new version of the container gets deployed with this pipeline:
&lt;figure>
&lt;img src="/images/rollingupdate-default.gif"/>
&lt;/figure>
&lt;/p>
&lt;h2 id="can-we-change-how-containers-are-deployed">Can we change how containers are deployed?&lt;/h2>
&lt;p>In our example so far, we observed that we go down to 3 running pods and up to 5 non &lt;em>Terminating&lt;/em> pods existing simultaneously.
You actually have some control over how Kubernetes handles the pod creation.
Two &lt;a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#rolling-update-deployment">parameters&lt;/a> will help you:&lt;/p>
&lt;ul>
&lt;li>&lt;code>maxSurge&lt;/code> sets a limit on how many pods can be created over the desired number of pods. Default is 25%.&lt;/li>
&lt;li>&lt;code>maxUnavailable&lt;/code> sets a limit on how many pods can be non-running. Default is also 25%.&lt;/li>
&lt;/ul>
&lt;p>Let&amp;rsquo;s modify our deployment manifest and ensure that we always have 4 pods running during deployment:&lt;/p>
&lt;figure>
&lt;img src="/images/kubernetes_deployments_3.png"/>
&lt;/figure>
&lt;p>Let&amp;rsquo;s see how we deploy with our new configuration:&lt;/p>
&lt;figure>
&lt;img src="/images/rollingupdate-0.gif"/>
&lt;/figure>
&lt;p>We keep 4 pods running throughout the deployment and never more than 5 non &lt;em>Terminating&lt;/em> pods (4 pods + 25%) existing at any given time.&lt;/p></description></item><item><title>Continuous-Deployment: Deploy Applications to Kubernetes</title><link>/continuous-deployment/spinnaker-user-guides/kubernetes-v2/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/kubernetes-v2/</guid><description>
&lt;h2 id="overview-of-spinnakers-kubernetes-v2-provider">Overview of Spinnaker&amp;rsquo;s Kubernetes V2 Provider&lt;/h2>
&lt;p>The Kubernetes V2 provider is centered on delivering and promoting Kubernetes manifests to different Kubernetes environments. These manifests are delivered to Spinnaker using &lt;a href="https://www.spinnaker.io/reference/artifacts/in-kubernetes-v2/#kubernetes-objects-as-artifacts">artifacts&lt;/a> and applied to the target cluster using &lt;code>kubectl&lt;/code> in the background. Currently, there is support to supply artifacts through Git, GCS and Google Pub/Sub. The V2 provider manages the version of the artifacts deployed to Kubernetes by appending a string such as &lt;code>v421&lt;/code> to the resource that was deployed to Kubernetes. This allows for easy rollbacks and management of resources.&lt;/p>
&lt;h3 id="current-limitations">Current limitations&lt;/h3>
&lt;ul>
&lt;li>The only supported services for artifact delivery are Github, GCS, or Google Pub/Sub. S3, SQS, and SNS are currently not supported.&lt;/li>
&lt;li>Native Spinnaker deployment strategies such as blue/green, highlander, rolling blue/green deployment are not supported. If these strategies are desired consider using the deployment object in your manifests.&lt;/li>
&lt;li>While you&amp;rsquo;re able to deploy all Kubernetes resource types, the V2 provider only considers &lt;code>containers&lt;/code> and &lt;code>configMaps&lt;/code> for &lt;a href="https://www.spinnaker.io/reference/artifacts/in-kubernetes-v2/#kubernetes-objects-as-artifacts">binding to the deployed manifest&lt;/a>. &lt;code>secrets&lt;/code> and other resource types are coming soon.&lt;/li>
&lt;li>You cannot manually trigger the pipeline, you have to use Github triggers.&lt;/li>
&lt;/ul>
&lt;h3 id="available-manifest-based-stages">Available manifest-based stages&lt;/h3>
&lt;p>There are 4 stages that are available:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Deploy Manifest&lt;/strong> - Uses &lt;code>kubectl apply&lt;/code> to deploy a manifest. Spinnaker will wait until the manifest stabilizes in the Kubernetes cluster or reach the expected capacity on healthy pods.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Delete Manifests&lt;/strong> - Removes the manifests and their corresponding artifacts in kubernetes based on different types and labels.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Scale Manifests&lt;/strong> - Scales replica sets to a given static size.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Rollback Manifest&lt;/strong> - Allows rollback of a given Kubernetes artifact by a given number of versions. Helpful in cases of a failed deployment or failed manual QA.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="creating-a-kubernetes-v2-pipeline">Creating a Kubernetes V2 pipeline&lt;/h2>
&lt;h3 id="configuring-the-pipeline-trigger">Configuring the pipeline trigger&lt;/h3>
&lt;p>We&amp;rsquo;ll begin by creating a pipeline that is triggered from Kubernetes artifacts and delivered through Github. Below we&amp;rsquo;ll define two artifacts that will be deployed as Kubernetes manifests: &lt;code>deployment.yml&lt;/code> and &lt;code>config-map.yml&lt;/code> which are valid Kubernetes manifests. Make sure to select the source as &lt;code>Github&lt;/code>.&lt;/p>
&lt;figure>
&lt;img src="/images/page.png"/>
&lt;/figure>
&lt;p>After configuring the artifacts we&amp;rsquo;ll need to associate them with a Github trigger so the pipeline is triggered whenever there are modifications pushed to Github. For example, the pipeline below will only trigger when &lt;em>either&lt;/em> &lt;code>deployment.yml&lt;/code> &lt;em>or&lt;/em> &lt;code>config-map.yml&lt;/code> are modified and pushed to the repository. If the manifest isn&amp;rsquo;t modified it&amp;rsquo;ll use the latest version that was deployed.&lt;/p>
&lt;blockquote>
&lt;p>Note: If you haven&amp;rsquo;t already, you&amp;rsquo;ll need to configure &lt;a href="https://www.spinnaker.io/setup/features/notifications/#github">Github webhooks&lt;/a> for your Spinnaker instance.&lt;/p>
&lt;/blockquote>
&lt;figure>
&lt;img src="/images/trigger.png"/>
&lt;/figure>
&lt;h3 id="configuring-the-config-map-manifest-delivery">Configuring the config map manifest delivery&lt;/h3>
&lt;p>We&amp;rsquo;ll configure the &lt;code>configMap&lt;/code> to be deployed first. Add a &lt;code>Deploy (Manifest)&lt;/code> stage to your pipelines.&lt;/p>
&lt;figure>
&lt;img src="/images/deploy_manifest.png"/>
&lt;/figure>
&lt;p>Once you&amp;rsquo;ve added the stage, select &lt;code>Artifact&lt;/code> from the &lt;code>Manifest Source&lt;/code> below and it will allow you to choose one of the expected artifacts that we configured in the previous section. Choose &lt;code>config-map.yml&lt;/code> and hit &lt;code>save&lt;/code>. Spinnaker will deploy the chosen artifact but append a version to the name of the artifact. For &lt;a href="https://github.com/armory/spinnaker-k8s-v2-example/blob/master/manifests/demo.yml">our example config map&lt;/a>. So for the name &lt;code>k8-v2-config-map&lt;/code> it will appear in the Kubernetes cluster with &lt;code>k8-v2-config-map-v001&lt;/code>.&lt;/p>
&lt;figure>
&lt;img src="/images/config-map.png"/>
&lt;/figure>
&lt;h3 id="configuring-deployment-manifest-delivery">Configuring deployment manifest delivery&lt;/h3>
&lt;p>Next we&amp;rsquo;ll configure a new &lt;code>Deploy (Manifest)&lt;/code> stage to deploy the &lt;a href="https://github.com/armory/spinnaker-k8s-v2-example/blob/master/manifests/demo.yml">deployment.yml&lt;/a> manifest. This manifest references our config-map as a volume and it&amp;rsquo;s source will be replaced by the versioned artifact deployed in the previous step: &lt;code>k8-v2-config-map-v001&lt;/code>. So if our &lt;code>deployment.yml&lt;/code> contains the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-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:#ff79c6">volumes&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ff79c6">name&lt;/span>: k8-config
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">configMap&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">name&lt;/span>: k8-v2-config-map
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>the name will be replaced with the properly versioned artifact:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-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:#ff79c6">volumes&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ff79c6">name&lt;/span>: k8-config
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">configMap&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">name&lt;/span>: k8-v2-config-map-v000
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;figure>
&lt;img src="/images/Image-2018-01-26-at-5.36.53-PM.png"/>
&lt;/figure>
&lt;h3 id="executing-the-pipeline">Executing the pipeline&lt;/h3>
&lt;p>Your final pipeline should look similar to the one below.
&lt;figure>
&lt;img src="/images/pipeline.png"/>
&lt;/figure>
&lt;/p>
&lt;p>In order to execute your pipeline the first time you&amp;rsquo;ll need to edit both the &lt;code>config-map.yml&lt;/code> and &lt;code>deployment.yml&lt;/code>, commit the changes to git and push the changes to Github. The pipeline should trigger and execute.&lt;/p></description></item><item><title>Continuous-Deployment: Manage Spinnaker Application Secrets Using HashiCorp Vault</title><link>/continuous-deployment/spinnaker-user-guides/app-secrets/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/app-secrets/</guid><description>
&lt;h2 id="overview-of-storing-secrets">Overview of storing secrets&lt;/h2>
&lt;p>Managing application secrets, such as database passwords and API keys, can
be tricky. These secrets should not be saved into the application&amp;rsquo;s source
code, but they do need to be made available to the application when it runs.&lt;/p>
&lt;p>Several tools have been built to address this issue. In this document, we&amp;rsquo;re
going to focus on how one might use &lt;a href="https://www.vaultproject.io/">HashiCorp Vault&lt;/a>
to act as our secrets management utility, and then set up our application and
clusters to pull secrets from there.&lt;/p>
&lt;p>In this guide, we&amp;rsquo;ll be using Vault as our example secrets manager, but a
similar pattern should be applicable to similar tools, such as Amazon Secrets
Manager.&lt;/p>
&lt;p>This guide presumes you&amp;rsquo;ve already installed and configured Vault; if you
are using Vault and have not done so, check Vault&amp;rsquo;s
&lt;a href="https://learn.hashicorp.com/vault/">Getting Started&lt;/a> guide.&lt;/p>
&lt;h2 id="protecting-application-secrets">Protecting application secrets&lt;/h2>
&lt;p>Armory recommends that you do not pass secrets through Spinnaker in plain text as this is not safe from a security standpoint. If your Spinnaker deployment gets breached and secrets were passed through it, intruders now have all applications secrets that were passed.&lt;/p>
&lt;p>Instead, use a secret store and only pass the location of or references to the secret. The best practice for using application secrets is for the application to fetch the secret during application startup. For VMs, this is during the VM bootstrap or application startup process. For Kubernetes, you usually do this using an init-container, sidecar, or both.&lt;/p>
&lt;p>For Vault, refer to the following resources about injecting application secrets securely into Kubernetes pods:&lt;/p>
&lt;ul>
&lt;li>HashiCorp - &lt;a href="https://www.hashicorp.com/blog/injecting-vault-secrets-into-kubernetes-pods-via-a-sidecar/">Injecting Vault Secrets Into Kubernetes Pods via a Sidecar&lt;/a>&lt;/li>
&lt;li>Banzai Cloud - &lt;a href="https://banzaicloud.com/blog/inject-secrets-into-pods-vault-revisited/">Inject secrets directly into Pods from Vault revisited&lt;/a>&lt;/li>
&lt;li>IT Next - &lt;a href="https://itnext.io/dynamic-vault-secrets-agent-sidecar-on-kubernetes-cc0ce3e54a94">Dynamic Vault Secrets — Agent Sidecar on Kubernetes&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>For more general information, see the following pages about AWS Secrets Manager and Vault:&lt;/p>
&lt;ul>
&lt;li>GoDaddy Engineering blog about their use of &lt;a href="https://www.godaddy.com/engineering/2019/04/16/kubernetes-external-secrets/">Kubernetes External Secrets&lt;/a>&lt;/li>
&lt;li>GoDaddy GitHub page about their open sourced implementation of &lt;a href="https://github.com/godaddy/kubernetes-external-secrets">Kubernetes External Secrets&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="basic-outline">Basic outline&lt;/h2>
&lt;p>The basic plan here is to allow developers to write code that references
&amp;ldquo;secrets&amp;rdquo; but which aren&amp;rsquo;t checked into the codebase. Often this will be
via a configuration file, or environment variables. For testing purposes,
this data may be changed out; developers may have their own personal set of
&amp;ldquo;secrets&amp;rdquo; for their development environment. The code just relies on the
file or env vars being set properly.&lt;/p>
&lt;p>An operations person (or team) is then responsible for the actual production
systems, including the passwords used. It&amp;rsquo;s their job to maintain those
secrets and make sure they are set correctly in the production environment.&lt;/p>
&lt;p>The plan, then, is to use a Kubernetes secret (which the developers should
not have access to) to store the Vault auth token (managed solely by an
Operations person). The app can use the auth token to retrieve the application
secrets from Vault at runtime. Spinnaker acts as a limited-use interface
such that the developer can kick off deployments to production, but may not
have actual direct access to the credentials for production.&lt;/p>
&lt;p>The following diagram attempts to outline this design, showing the barrier
between the developer and the actual secrets their code uses:&lt;/p>
&lt;figure>
&lt;img src="/images/app_secrets_diagram.png"/>
&lt;/figure>
&lt;blockquote>
&lt;p>NOTE: Vault (and other tools) have taken this security a step further by connecting the secrets service with the backend systems such that the actual password being used is cycled automatically and is never actually known to any specific person. If your environment permits this setup, it&amp;rsquo;s definitely a more secure mechanism of password management, but it&amp;rsquo;s also a much more complicated topic, and may not be possible depending on what services you are attempting to secure.&lt;/p>
&lt;/blockquote>
&lt;h2 id="operations-side-configuration">Operations-side configuration&lt;/h2>
&lt;p>The first step to securing the secrets is to create a policy in Vault, apply
the policy to the Vault path(s) that contain the secrets, and create a service
account that is associated with that policy to access it. The authorization
token for this account is what we&amp;rsquo;ll push into the deployed pods via a
Kubernetes Secret, and will allow that pod to request the current secrets
from Vault.&lt;/p>
&lt;h3 id="set-up-a-secret-path">Set up a secret path&lt;/h3>
&lt;p>Within Vault, set up your secrets path (such as &lt;code>/secrets&lt;/code> or &lt;code>/prod/secrets&lt;/code>
or whatever works for you). Don&amp;rsquo;t put any secrets in yet; you&amp;rsquo;ll want to
secure the path first (next step)&lt;/p>
&lt;h3 id="secure-path-with-a-policy">Secure path with a policy&lt;/h3>
&lt;p>Create a policy by name that has access to the path you created. By default,
paths are open to anyone authenticated, but as soon as you add a policy to
the path, it&amp;rsquo;s locked down to only those accounts with that policy.&lt;/p>
&lt;p>You can read more about Vault policies &lt;a href="https://www.vaultproject.io/docs/concepts/policies.html">here&lt;/a>.&lt;/p>
&lt;h3 id="create-service-account-token">Create service account token&lt;/h3>
&lt;p>Use &lt;code>vault token create&lt;/code> to create a token attached to the policy you just
created (&lt;a href="https://www.vaultproject.io/docs/commands/token/create.html">reference&lt;/a>).
Your app, using this token, should then have access to the actual secrets.&lt;/p>
&lt;p>You can now put in your production secrets into that &amp;ldquo;bucket&amp;rdquo; safely.&lt;/p>
&lt;h3 id="create-a-kubernetes-secret">Create a Kubernetes secret&lt;/h3>
&lt;p>Using &lt;code>kubectl&lt;/code> you&amp;rsquo;ll want to create a Secret within your namespace that
contains this token. The &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/">Kubernetes documentation&lt;/a>
discusses in detail the different ways you may create a secret. Also note
this documentation discusses how to then mount/use the secret within the
pod.&lt;/p>
&lt;h2 id="bringing-it-all-together">Bringing it all together&lt;/h2>
&lt;p>Now that your ops team has secured your secrets in Vault, and set up the
Kubernetes secret, the next step is to have your Kubernetes deployment
mount the auth token secret, and then finally, arrange for your app to
use Vault to retrieve the actual production secrets.&lt;/p>
&lt;h3 id="configure-your-manifest">Configure your manifest&lt;/h3>
&lt;p>To make the Vault token available to your application, you&amp;rsquo;ll need to mount
the secret within your manifest. The
&lt;a href="https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/">Kubernetes documentation&lt;/a>
shows a number of ways in which you can configure your pods to get access to
the Kubernetes secrets either as a mounted volume or as an environment
variable.&lt;/p>
&lt;p>This manifest reference may be visible to developers, but provided developers
do not have direct access to the Kubernetes cluster, they won&amp;rsquo;t be able to
retrieve the actual token from the secret; Spinnaker allows the developers to
still manage the deployments to production, but not to retrieve the secret
itself.&lt;/p>
&lt;h3 id="configure-your-application">Configure your application&lt;/h3>
&lt;p>Now your code (or perhaps just a bootup script) can be written/configured to
grab the Vault token that was mounted in your Manifest, and, combining that
with the Vault paths, can retrieve your secrets safely.&lt;/p>
&lt;p>One way to do this is with a Kubernetes &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/">init container&lt;/a>
intended to run the Vault image (which provides the &lt;code>vault&lt;/code> command line tool)
to log in, retrieve the secrets and write them to disk, where your application
can pick them up.&lt;/p>
&lt;p>Alternatively, your code may be able to access Vault directly with the access
token, storing the secrets retrieved in application memory, where it&amp;rsquo;s not
readily accessible by someone who manages to gain access to the container&amp;rsquo;s
shell. Hashicorp provides &lt;a href="https://www.vaultproject.io/api/index.html">documentation on the Vault API&lt;/a>&lt;/p>
&lt;h2 id="alternatives">Alternatives&lt;/h2>
&lt;p>We&amp;rsquo;ve only scratched the surface of ways in which app secrets can be managed
and then merged at deploy time by Spinnaker. We&amp;rsquo;ve used Vault as an example
because it&amp;rsquo;s being used more and more by our customers, but there are other
products (like Amazon Secrets Manager) and, of course, homebrewed solutions.&lt;/p>
&lt;p>In all cases, Spinnaker provides a terrific way to give developers the ability
to manage their own deployments while still securing production passwords
separately.&lt;/p>
&lt;h2 id="see-also">See also&lt;/h2>
&lt;p>Here are some other resources that may provide additional insight into how
to manage application secrets within your system:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/Boostport/kubernetes-vault">Boostport&amp;rsquo;s Kubernetes Vault Integration&lt;/a> &amp;ndash; This Github project incorporates a controller that watches
for new pods and injects the secrets into them when they initialize.&lt;/li>
&lt;li>&lt;a href="https://medium.com/ww-engineering/working-with-vault-secrets-on-kubernetes-fde381137d88">Working With Vault Secrets on Kubernetes&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Continuous-Deployment: Migrating Armory CD from Operator to Kustomize Deployment</title><link>/continuous-deployment/spinnaker-user-guides/armory-operator-to-kustomize-migration/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/armory-operator-to-kustomize-migration/</guid><description>
&lt;h2 id="migrating-armory-cd-from-operator-to-kustomize-deployment">Migrating Armory CD from Operator to Kustomize Deployment&lt;/h2>
&lt;h3 id="introduction">Introduction&lt;/h3>
&lt;p>This document provides step-by-step instructions for migrating your Spinnaker installation from using the Operator deployment method to a native Kubernetes deployment using Kustomize. This approach gives you more direct control over your Spinnaker resources and removes the dependency on the Operator.&lt;/p>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Important&lt;/h4>
Please thoroughly test this migration in a non-production environment before deploying to production.
&lt;/div>
&lt;h4 id="prerequisites">Prerequisites&lt;/h4>
&lt;ul>
&lt;li>Kubectl command-line tool installed and configured to access your cluster&lt;/li>
&lt;li>Basic understanding of Kubernetes resources (deployments, services, configmaps)&lt;/li>
&lt;li>Access to the current Spinnaker namespace&lt;/li>
&lt;/ul>
&lt;h4 id="migration-process-overview">Migration Process Overview&lt;/h4>
&lt;ol>
&lt;li>Download current configuration files and Kubernetes resources&lt;/li>
&lt;li>Set up Kustomize structure for native deployment&lt;/li>
&lt;li>Remove Operator ownership from services&lt;/li>
&lt;li>Scale down the Operator&lt;/li>
&lt;li>Deploy using Kustomize&lt;/li>
&lt;li>Validate the deployment&lt;/li>
&lt;li>Remove Operator and CRDs (after confirming stability)&lt;/li>
&lt;/ol>
&lt;h5 id="step-1-download-current-configuration">Step 1: Download Current Configuration&lt;/h5>
&lt;p>The script provided below will download:&lt;/p>
&lt;ul>
&lt;li>All configuration files located in /opt/spinnaker/config from each service&lt;/li>
&lt;li>All deployment, service, and statefulset YAML files for each service&lt;/li>
&lt;/ul>
&lt;h6 id="how-to-use-the-download-script">How to Use the Download Script&lt;/h6>
&lt;ol>
&lt;li>Save the script at the bottom of this document to a file named &lt;code>download_spinnaker_configs.sh&lt;/code>&lt;/li>
&lt;li>Make the script executable:
&lt;code>chmod +x download_spinnaker_configs.sh&lt;/code>&lt;/li>
&lt;li>Run the script with your Spinnaker namespace:
&lt;code>./download_spinnaker_configs.sh your-spinnaker-namespace&lt;/code>&lt;/li>
&lt;li>The script will create an operator-migration directory containing all needed files&lt;/li>
&lt;/ol>
&lt;h6 id="what-gets-downloaded">What Gets Downloaded&lt;/h6>
&lt;ul>
&lt;li>&lt;strong>Deployments:&lt;/strong> YAML files for each service deployment&lt;/li>
&lt;li>&lt;strong>Services:&lt;/strong> YAML files for all Spinnaker services&lt;/li>
&lt;li>&lt;strong>StatefulSets:&lt;/strong> YAML files for any statefulsets (like front50)&lt;/li>
&lt;li>&lt;strong>Configuration Files:&lt;/strong> All files from /opt/spinnaker/config in each pod&lt;/li>
&lt;/ul>
&lt;h5 id="step-2-set-up-kustomize-structure">Step 2: Set Up Kustomize Structure&lt;/h5>
&lt;p>Create a Kustomize directory structure for your Spinnaker deployment:&lt;/p>
&lt;ol>
&lt;li>Move the downloaded deployments and services to their respective directories&lt;/li>
&lt;li>Create configmaps from the downloaded configuration files&lt;/li>
&lt;li>Set up the kustomization.yaml files&lt;/li>
&lt;/ol>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Tip&lt;/h4>
You can use the GitHub - spinnaker/spinnaker-kustomize: Spinnaker installation via kustomize as a reference for Kustomize structure
&lt;/div>
&lt;h5 id="step-3-remove-operator-ownership-from-services">Step 3: Remove Operator Ownership from Services&lt;/h5>
&lt;p>This step detaches the Operator&amp;rsquo;s control while keeping services running.&lt;/p>
&lt;ol>
&lt;li>Identify resources owned by the Operator:
&lt;code>kubectl get all -n your-spinnaker-namespace -o json | jq '.items[] | select(.metadata.ownerReferences[]? | .apiVersion==&amp;quot;spinnaker.armory.io/v1alpha2&amp;quot; and .kind==&amp;quot;SpinnakerService&amp;quot;) | {name: .metadata.name, kind: .kind}'&lt;/code>&lt;/li>
&lt;li>Remove ownership references using patch commands:
&lt;code>kubectl patch deployment spin-deck -n your-spinnaker-namespace --type json -p='[{&amp;quot;op&amp;quot;: &amp;quot;remove&amp;quot;, &amp;quot;path&amp;quot;: &amp;quot;/metadata/ownerReferences&amp;quot;}]'&lt;/code>&lt;/li>
&lt;li>Repeat for all resources with Operator ownership
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Note&lt;/h4>
This breaks the connection between the Operator and services but keeps everything running
&lt;/div>
&lt;/li>
&lt;/ol>
&lt;h5 id="step-4-verify-ownership-removal">Step 4: Verify Ownership Removal&lt;/h5>
&lt;p>Confirm that no resources are still owned by the Operator:
&lt;code>kubectl get all -n your-spinnaker-namespace -o json | jq '.items[] | select(.metadata.ownerReferences[]? | .apiVersion==&amp;quot;spinnaker.armory.io/v1alpha2&amp;quot; and .kind==&amp;quot;SpinnakerService&amp;quot;) | {name: .metadata.name, kind: .kind}'&lt;/code>
The command should return empty if all ownership references have been removed.&lt;/p>
&lt;h5 id="step-5-extra-precautions-before-deployment">Step 5: Extra Precautions Before Deployment&lt;/h5>
&lt;p>Compare current resources with your Kustomize configurations:
&lt;code>kubectl diff -f &amp;lt;(kustomize build ./overlays/prod)&lt;/code>&lt;/p>
&lt;p>Review the differences carefully. Look for:&lt;/p>
&lt;ul>
&lt;li>Immutable field changes (might require special handling)&lt;/li>
&lt;li>Configuration changes that could affect service behavior&lt;/li>
&lt;li>Missing resources that should be included&lt;/li>
&lt;/ul>
&lt;h6 id="perform-a-dry-run">Perform a Dry Run&lt;/h6>
&lt;p>Test your deployment without actually applying changes:
&lt;code>kubectl apply --dry-run=client -f &amp;lt;(kustomize build ./overlays/prod)&lt;/code>&lt;/p>
&lt;h5 id="step-6-scale-down-the-operator">Step 6: Scale Down the Operator&lt;/h5>
&lt;p>Prevent the Operator from interfering with your deployment:
&lt;code>kubectl scale deployment spinnaker-operator -n your-spinnaker-namespace --replicas=0&lt;/code>&lt;/p>
&lt;h5 id="step-7-deploy-using-kustomize">Step 7: Deploy Using Kustomize&lt;/h5>
&lt;p>Apply your Kustomize configurations:&lt;/p>
&lt;p>&lt;code>kubectl apply -f &amp;lt;(kustomize build ./overlays/prod)&lt;/code>&lt;/p>
&lt;h5 id="step-8-validate-and-monitor">Step 8: Validate and Monitor&lt;/h5>
&lt;ol>
&lt;li>Check that all pods are running:
&lt;code>kubectl get pods -n your-spinnaker-namespace&lt;/code>&lt;/li>
&lt;li>Verify Spinnaker services are accessible:
&lt;ul>
&lt;li>Access the Spinnaker UI&lt;/li>
&lt;li>Test a simple pipeline&lt;/li>
&lt;li>Check integrations are working&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Monitor the environment for stability over the next few days&lt;/li>
&lt;/ol>
&lt;h5 id="step-9-remove-operator-and-crds">Step 9: Remove Operator and CRDs&lt;/h5>
&lt;p>Once stability is confirmed (at least 24 hours later):&lt;/p>
&lt;ol>
&lt;li>Remove the Operator CRDs:
&lt;code>kubectl delete crd spinnakerservices.spinnaker.armory.io&lt;/code>&lt;/li>
&lt;li>Remove the Operator deployment if still present:
&lt;code>kubectl delete deployment spinnaker-operator -n your-spinnaker-namespace&lt;/code>&lt;/li>
&lt;/ol>
&lt;h5 id="rollback-plan">Rollback Plan&lt;/h5>
&lt;p>If issues arise during migration:&lt;/p>
&lt;ol>
&lt;li>Scale up the Operator:
&lt;code>kubectl scale deployment spinnaker-operator -n your-spinnaker-namespace --replicas=1&lt;/code>&lt;/li>
&lt;li>Reapply the previous SpinnakerService resource:
&lt;code>kubectl apply -f original-spinnakerservice.yaml&lt;/code>&lt;/li>
&lt;li>Allow the Operator to reconcile and restore the previous state&lt;/li>
&lt;/ol>
&lt;h4 id="download-script">Download Script&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6272a4"># Script to download ONLY files from /opt/spinnaker/config in Spinnaker pods&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:#8be9fd;font-style:italic">set&lt;/span> -e
&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:#ff79c6">if&lt;/span> &lt;span style="color:#ff79c6">[&lt;/span> -z &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$1&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> &lt;span style="color:#ff79c6">]&lt;/span>; &lt;span style="color:#ff79c6">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Please provide a namespace&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Usage: &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$0&lt;/span>&lt;span style="color:#f1fa8c"> &amp;lt;namespace&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">exit&lt;/span> &lt;span style="color:#bd93f9">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">fi&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:#8be9fd;font-style:italic">NAMESPACE&lt;/span>&lt;span style="color:#ff79c6">=&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">OUTPUT_DIR&lt;/span>&lt;span style="color:#ff79c6">=&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;operator-migration&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 style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Creating output directory: &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>rm -rf &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mkdir -p &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">&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 style="color:#6272a4"># 1. First download deployments and services&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Downloading Kubernetes deployments and services...&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 style="color:#6272a4"># Download Deployments&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Downloading deployments...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mkdir -p &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">/deployments&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl get deployments -n &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$NAMESPACE&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -o name | &lt;span style="color:#ff79c6">while&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">read&lt;/span> -r deployment; &lt;span style="color:#ff79c6">do&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">deployment_name&lt;/span>&lt;span style="color:#ff79c6">=&lt;/span>&lt;span style="color:#ff79c6">$(&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$deployment&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> | cut -d/ -f2&lt;span style="color:#ff79c6">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Downloading deployment: &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$deployment_name&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kubectl get deployment &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$deployment_name&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -n &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$NAMESPACE&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -o yaml &amp;gt; &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">/deployments/&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$deployment_name&lt;/span>&lt;span style="color:#f1fa8c">.yaml&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">done&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:#6272a4"># Download Services&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Downloading services...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mkdir -p &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">/services&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl get services -n &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$NAMESPACE&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -o name | &lt;span style="color:#ff79c6">while&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">read&lt;/span> -r service; &lt;span style="color:#ff79c6">do&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">service_name&lt;/span>&lt;span style="color:#ff79c6">=&lt;/span>&lt;span style="color:#ff79c6">$(&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$service&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> | cut -d/ -f2&lt;span style="color:#ff79c6">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Downloading service: &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$service_name&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kubectl get service &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$service_name&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -n &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$NAMESPACE&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -o yaml &amp;gt; &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">/services/&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$service_name&lt;/span>&lt;span style="color:#f1fa8c">.yaml&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">done&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:#6272a4"># Download StatefulSets if any&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Checking for statefulsets...&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">if&lt;/span> kubectl get statefulsets -n &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$NAMESPACE&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> 2&amp;gt;/dev/null | grep -q .; &lt;span style="color:#ff79c6">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mkdir -p &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">/statefulsets&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kubectl get statefulsets -n &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$NAMESPACE&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -o name | &lt;span style="color:#ff79c6">while&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">read&lt;/span> -r statefulset; &lt;span style="color:#ff79c6">do&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">statefulset_name&lt;/span>&lt;span style="color:#ff79c6">=&lt;/span>&lt;span style="color:#ff79c6">$(&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$statefulset&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> | cut -d/ -f2&lt;span style="color:#ff79c6">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Downloading statefulset: &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$statefulset_name&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kubectl get statefulset &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$statefulset_name&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -n &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$NAMESPACE&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -o yaml &amp;gt; &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">/statefulsets/&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$statefulset_name&lt;/span>&lt;span style="color:#f1fa8c">.yaml&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">done&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">fi&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:#6272a4"># 2. Now download files from /opt/spinnaker/config&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Downloading files ONLY from /opt/spinnaker/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 style="color:#6272a4"># Get all pods&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">PODS&lt;/span>&lt;span style="color:#ff79c6">=&lt;/span>&lt;span style="color:#ff79c6">$(&lt;/span>kubectl get pods -n &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$NAMESPACE&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -o name | cut -d/ -f2&lt;span style="color:#ff79c6">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">for&lt;/span> POD in &lt;span style="color:#8be9fd;font-style:italic">$PODS&lt;/span>; &lt;span style="color:#ff79c6">do&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">SERVICE&lt;/span>&lt;span style="color:#ff79c6">=&lt;/span>&lt;span style="color:#ff79c6">$(&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$POD&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> | sed -E &lt;span style="color:#f1fa8c">&amp;#39;s/([a-z-]+)-[0-9a-z-]+.*/\1/&amp;#39;&lt;/span>&lt;span style="color:#ff79c6">)&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:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Processing pod: &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$POD&lt;/span>&lt;span style="color:#f1fa8c"> (service: &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$SERVICE&lt;/span>&lt;span style="color:#f1fa8c">)&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> mkdir -p &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">/&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$SERVICE&lt;/span>&lt;span style="color:#f1fa8c">&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 style="color:#6272a4"># Check ONLY for /opt/spinnaker/config&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">if&lt;/span> kubectl &lt;span style="color:#8be9fd;font-style:italic">exec&lt;/span> -n &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$NAMESPACE&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$POD&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -- ls -la /opt/spinnaker/config &amp;amp;&amp;gt;/dev/null; &lt;span style="color:#ff79c6">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Found /opt/spinnaker/config directory in &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$POD&lt;/span>&lt;span style="color:#f1fa8c">&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 style="color:#6272a4"># List all files first&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">CONFIG_FILES&lt;/span>&lt;span style="color:#ff79c6">=&lt;/span>&lt;span style="color:#ff79c6">$(&lt;/span>kubectl &lt;span style="color:#8be9fd;font-style:italic">exec&lt;/span> -n &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$NAMESPACE&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$POD&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -- find /opt/spinnaker/config -type f 2&amp;gt;/dev/null&lt;span style="color:#ff79c6">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">if&lt;/span> &lt;span style="color:#ff79c6">[&lt;/span> -z &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$CONFIG_FILES&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> &lt;span style="color:#ff79c6">]&lt;/span>; &lt;span style="color:#ff79c6">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;No files found in /opt/spinnaker/config for &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$POD&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">continue&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">fi&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:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Found &lt;/span>&lt;span style="color:#ff79c6">$(&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$CONFIG_FILES&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> | wc -l | tr -d &lt;span style="color:#f1fa8c">&amp;#39; &amp;#39;&lt;/span>&lt;span style="color:#ff79c6">)&lt;/span>&lt;span style="color:#f1fa8c"> files in /opt/spinnaker/config for &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$POD&lt;/span>&lt;span style="color:#f1fa8c">&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 style="color:#6272a4"># Download each file&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">for&lt;/span> FILE in &lt;span style="color:#8be9fd;font-style:italic">$CONFIG_FILES&lt;/span>; &lt;span style="color:#ff79c6">do&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">FILENAME&lt;/span>&lt;span style="color:#ff79c6">=&lt;/span>&lt;span style="color:#ff79c6">$(&lt;/span>basename &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$FILE&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#ff79c6">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Downloading &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$FILENAME&lt;/span>&lt;span style="color:#f1fa8c"> from &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$POD&lt;/span>&lt;span style="color:#f1fa8c">&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 style="color:#8be9fd;font-style:italic">FILE_CONTENT&lt;/span>&lt;span style="color:#ff79c6">=&lt;/span>&lt;span style="color:#ff79c6">$(&lt;/span>kubectl &lt;span style="color:#8be9fd;font-style:italic">exec&lt;/span> -n &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$NAMESPACE&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$POD&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -- cat &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$FILE&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> 2&amp;gt;/dev/null&lt;span style="color:#ff79c6">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">if&lt;/span> &lt;span style="color:#ff79c6">[&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">$?&lt;/span> -eq &lt;span style="color:#bd93f9">0&lt;/span> &lt;span style="color:#ff79c6">]&lt;/span> &lt;span style="color:#ff79c6">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#ff79c6">[&lt;/span> -n &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$FILE_CONTENT&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> &lt;span style="color:#ff79c6">]&lt;/span>; &lt;span style="color:#ff79c6">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$FILE_CONTENT&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> &amp;gt; &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">/&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$SERVICE&lt;/span>&lt;span style="color:#f1fa8c">/&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$FILENAME&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Saved &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$FILENAME&lt;/span>&lt;span style="color:#f1fa8c"> to &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">/&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$SERVICE&lt;/span>&lt;span style="color:#f1fa8c">/&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$FILENAME&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Failed to download &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$FILENAME&lt;/span>&lt;span style="color:#f1fa8c"> or file is empty&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">done&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:#6272a4"># Check if we downloaded any files&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">if&lt;/span> &lt;span style="color:#ff79c6">[&lt;/span> -z &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#ff79c6">$(&lt;/span>ls -A &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">/&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$SERVICE&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> 2&amp;gt;/dev/null&lt;span style="color:#ff79c6">)&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> &lt;span style="color:#ff79c6">]&lt;/span>; &lt;span style="color:#ff79c6">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;No files were successfully downloaded from &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$POD&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Successfully downloaded &lt;/span>&lt;span style="color:#ff79c6">$(&lt;/span>ls -1 &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">/&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$SERVICE&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> | wc -l | tr -d &lt;span style="color:#f1fa8c">&amp;#39; &amp;#39;&lt;/span>&lt;span style="color:#ff79c6">)&lt;/span>&lt;span style="color:#f1fa8c"> files from &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$POD&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;No /opt/spinnaker/config directory found in &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$POD&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">done&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:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;======================================&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Download completed. Files saved to: &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Summary of downloaded files by service:&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 style="color:#6272a4"># Generate summary&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">for&lt;/span> DIR in &lt;span style="color:#ff79c6">$(&lt;/span>find &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$OUTPUT_DIR&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -mindepth &lt;span style="color:#bd93f9">1&lt;/span> -maxdepth &lt;span style="color:#bd93f9">1&lt;/span> -type d | sort&lt;span style="color:#ff79c6">)&lt;/span>; &lt;span style="color:#ff79c6">do&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">SERVICE&lt;/span>&lt;span style="color:#ff79c6">=&lt;/span>&lt;span style="color:#ff79c6">$(&lt;/span>basename &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$DIR&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#ff79c6">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">FILE_COUNT&lt;/span>&lt;span style="color:#ff79c6">=&lt;/span>&lt;span style="color:#ff79c6">$(&lt;/span>find &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$DIR&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -type f | wc -l&lt;span style="color:#ff79c6">)&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:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;- &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$SERVICE&lt;/span>&lt;span style="color:#f1fa8c">: &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$FILE_COUNT&lt;/span>&lt;span style="color:#f1fa8c"> files&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">if&lt;/span> &lt;span style="color:#ff79c6">[&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$FILE_COUNT&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> -gt &lt;span style="color:#bd93f9">0&lt;/span> &lt;span style="color:#ff79c6">]&lt;/span>; &lt;span style="color:#ff79c6">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ls -1 &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$DIR&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span> | sort | &lt;span style="color:#ff79c6">while&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">read&lt;/span> -r file; &lt;span style="color:#ff79c6">do&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34; - &lt;/span>&lt;span style="color:#8be9fd;font-style:italic">$file&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">done&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">done&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:#8be9fd;font-style:italic">echo&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;Script execution complete!&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>By following these steps, you&amp;rsquo;ll successfully migrate from the Spinnaker Operator to a native Kubernetes deployment using Kustomize. This approach gives you more direct control over your Spinnaker resources and eliminates dependency on the Operator.
If you encounter any issues during the migration process, please submit a support ticket for assistance.&lt;/p></description></item><item><title>Continuous-Deployment: Spinnaker Pipelines</title><link>/continuous-deployment/spinnaker-user-guides/spin-pipelines/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/spin-pipelines/</guid><description>
&lt;h2 id="overview-of-spinnaker-pipelines">Overview of Spinnaker pipelines&lt;/h2>
&lt;p>Pipelines are a combination of stages that enable very sophisticated coordination and branching. They are the key to orchestrating deploys in Spinnaker and each one is specific to an application. To see an application&amp;rsquo;s pipelines, select &amp;lsquo;Applications&amp;rsquo; from Spinnaker&amp;rsquo;s top navigation bar, click on an application&amp;rsquo;s name, and then press the &amp;lsquo;Pipelines&amp;rsquo; tab. The result from a pipeline running is called an execution.&lt;/p>
&lt;p>Take this screenshot for example:&lt;/p>
&lt;figure>
&lt;img src="/images/pipelines-deploy-2-executions.png"/>
&lt;/figure>
&lt;p>There is a pipeline called &amp;lsquo;Deploy&amp;rsquo; with two executions, both labeled &amp;lsquo;Manual Start&amp;rsquo;. The top execution is marked as &amp;lsquo;Succeeded&amp;rsquo; while the bottom is marked as &amp;lsquo;Cancelled&amp;rsquo;.&lt;/p>
&lt;p>For more information on creating bake and deploy pipelines on AWS, checkout the &lt;a href="/continuous-deployment/spinnaker-user-guides/aws-guides/aws-baking-images/">baking&lt;/a> and &lt;a href="/continuous-deployment/spinnaker-user-guides/aws-guides/aws-deploy/">deploying&lt;/a> guides.&lt;/p>
&lt;h2 id="manual-execution">Manual execution&lt;/h2>
&lt;p>You can re-run an execution by pressing the &amp;lsquo;Start Manual Execution&amp;rsquo;.&lt;/p>
&lt;figure>
&lt;img src="/images/pipelines-rerun-manual-execution.png"/>
&lt;/figure>
&lt;p>If your pipeline has a Jenkins&amp;rsquo; trigger, you can select which Jenkins&amp;rsquo; build number to use for running the pipeline.&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-04-03-at-4.53.50-PM.png"/>
&lt;/figure>
&lt;p>The artifacts produced by the build you select will be used in the pipeline. If your pipeline bakes an image, a cached image will be used if available. To force a rebuild, make sure you specify such before pressing the &amp;lsquo;Run&amp;rsquo; button.&lt;/p>
&lt;h2 id="enabling-notifications">Enabling notifications&lt;/h2>
&lt;p>Spinnaker supports several methods of notification. Notifications can be made when a pipeline runs, succeeds, or fails. You can be contacted via SMS, email, slack, hipchat, and/or pagerduty. Each of these outlets need to be configured within Spinnaker by your Spinnaker Administrator. Once it is configured, you can enable it in your pipeline.&lt;/p>
&lt;p>To enable it, navigate to the configuration screen for your pipeline. Make sure you have the &amp;lsquo;Configuration&amp;rsquo; stage selected. Scroll down to the &amp;lsquo;Notifications&amp;rsquo; section.&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-04-03-at-4.31.37-PM.png"/>
&lt;/figure>
&lt;p>Press &amp;lsquo;Add Notifications Preference&amp;rsquo;. For example&amp;rsquo;s sake, I have selected to receive a notification via Slack in the &lt;code>#engineering&lt;/code> channel whenever my pipeline fails.&lt;/p>
&lt;p>Finally press &amp;lsquo;Update&amp;rsquo; to finish. Don&amp;rsquo;t forget to press &amp;lsquo;Save Changes&amp;rsquo; on your pipeline configuration.&lt;/p>
&lt;h2 id="pipeline-json">Pipeline JSON&lt;/h2>
&lt;p>Pipelines are represented as JSON behind the scenes. The JSON is interpreted and displayed to you in the UI. However, sometimes it is helpful to view or edit the JSON directly. To access the JSON:&lt;/p>
&lt;ol>
&lt;li>Click &amp;lsquo;Configure&amp;rsquo; on your pipeline:&lt;/li>
&lt;/ol>
&lt;figure>
&lt;img src="/images/Image-2017-05-04-at-4.23.33-PM.png"/>
&lt;/figure>
&lt;ol start="2">
&lt;li>Press the &amp;lsquo;Pipeline Actions&amp;rsquo; button in the upper right to display a dropdown menu.&lt;/li>
&lt;/ol>
&lt;figure>
&lt;img src="/images/Image-2017-05-04-at-4.30.11-PM.png"/>
&lt;/figure>
&lt;p>There are two JSON related options on this dropdown menu:&lt;/p>
&lt;p>a. If you select &amp;lsquo;Edit as JSON&amp;rsquo; then you should see something like:&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-05-04-at-4.32.03-PM.png"/>
&lt;/figure>
&lt;p>From this screen you can edit the JSON directly. &lt;strong>Remember to always save your changes&lt;/strong>, so they will be used in the next execution of your pipeline.&lt;/p>
&lt;p>b. If you select &amp;lsquo;Show Revision History&amp;rsquo; then you should see something like:&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-05-04-at-4.35.39-PM.png"/>
&lt;/figure>
&lt;p>You can select different revisions using the dropdown menu labeled &amp;lsquo;Revision&amp;rsquo; in the top left. You can compare it to different versions using the &amp;lsquo;compare to&amp;rsquo; dropdown menu in the upper right.&lt;/p>
&lt;h2 id="troubleshooting">Troubleshooting&lt;/h2>
&lt;h3 id="hanging-or-timed-out-pipelines">Hanging or timed out pipelines&lt;/h3>
&lt;p>A lot of the time pipelines hang because of a misconfigured stage. This is a common occurrence when a server group does not complete its deploy because the deployed instances never pass the health check. This happens both when the health check is misconfigured and/or when the image doesn&amp;rsquo;t bake as expected. These two areas should be investigated first. For more information you can see the troubleshooting topic in the &lt;a href="/continuous-deployment/spinnaker-user-guides/aws-guides/aws-deploy/">deployment guide&lt;/a>.&lt;/p></description></item><item><title>Continuous-Deployment: Spinnaker Best Practices</title><link>/continuous-deployment/spinnaker-user-guides/best-practices/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/best-practices/</guid><description>
&lt;h2 id="configuration-management">Configuration management&lt;/h2>
&lt;h3 id="baking-configuration">Baking configuration&lt;/h3>
&lt;p>It is common practice to bake the bulk of your application&amp;rsquo;s configuration into your AMI. Secrets should not be included. Configurations that frequently change and service discovery is often managed by another system. Some common system choices include but are not limited to: &lt;a href="https://www.consul.io/">Consul&lt;/a>, &lt;a href="https://github.com/Netflix/archaius">Archaius&lt;/a>, &lt;a href="https://zookeeper.apache.org/">Zookeeper&lt;/a>, and &lt;a href="https://github.com/coreos/etcd">etcd&lt;/a>.&lt;/p>
&lt;p>If you are running the default installation of Spinnaker, then every AWS instance will have details about its environment/stack stored in &lt;code>/etc/default/server-env&lt;/code>. This can be used to load the correct configuration.&lt;/p>
&lt;h3 id="service-discovery">Service discovery&lt;/h3>
&lt;p>While it is not feasible for every type of application, using a load balancer can greatly simplify things. Spinnaker gives load balancers first-class citizenship and will not need additional integrations to work right out of the box.&lt;/p>
&lt;p>For applications and workloads that cannot utilize a load balancer, the tools listed above may help.&lt;/p>
&lt;h2 id="secret-management">Secret management&lt;/h2>
&lt;p>Managing Secrets is a major part of deploying software. Spinnaker does not directly do this for you. However, there are still some guiding principles to help you along the way.&lt;/p>
&lt;p>Do not to bake Secrets into your images. If you have the default installation of Armory, then you can find &lt;code>/etc/default/server-env&lt;/code> on your instance. This file will have information (in the format of key-value pairs) about the environment and stack you are running on. You can use this to load the correct Secrets.&lt;/p>
&lt;p>Loading Secrets will have to be done at runtime since Secrets are not baked into images.&lt;/p>
&lt;p>Specifically for Secure Sockets Layer (SSL), it can be beneficial to terminate SSL at the Elastic Load Balancer (ELB) whenever feasible. Amazon has the &lt;a href="">Key Management Service (KMS)&lt;/a> for this purpose. If you need to handle certificate management at the application level, you might want to check out &lt;a href="http://techblog.netflix.com/2015/09/introducing-lemur.html">Netflix&amp;rsquo;s Lemur&lt;/a> project.&lt;/p>
&lt;p>Some other general purpose Secret Management tools include:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.vaultproject.io/">HashiCorp&amp;rsquo;s Vault&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://engineering.nike.com/cerberus/">Nike&amp;rsquo;s Cerberus&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="using-vault-with-spinnaker-on-aws">Using Vault With Spinnaker on AWS&lt;/h3>
&lt;p>Issuing a Vault token with AWS is an automated process that uses AWS as a &lt;a href="https://www.vaultproject.io/docs/auth/aws-ec2.html">trusted third party to initiate authorization&lt;/a>.&lt;/p>
&lt;p>One piece of &amp;ldquo;dynamic metadata&amp;rdquo; available to the EC2 instance, is the instance identity document, a JSON representation of a collection of instance metadata. AWS also provides PKCS#7 signature of the instance metadata document, and publishes the public keys (grouped by region) which can be used to verify the signature.&lt;/p>
&lt;p>You can issue an authentication by issuing the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>vault auth-enable aws-ec2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And then proceeding with any additional vault commands and execution that needs to happen. These steps need to happen at startup you&amp;rsquo;ll want add this script as a &lt;a href="/continuous-deployment/spinnaker-user-guides/aws-guides/aws-deploy/">base 64 encoded users-data in your deployment steps&lt;/a>.&lt;/p>
&lt;h3 id="isolate-delivery-pipelines-from-integration">Isolate delivery pipelines from integration&lt;/h3>
&lt;p>There are several ways to trigger a deployment pipeline. However, depending on the asset you are delivering, some methods are easier to work with than others.&lt;/p>
&lt;p>&lt;em>Docker Images&lt;/em>&lt;/p>
&lt;p>It is best to have Spinnaker trigger off of a push to a Docker registry instead of triggering off of a GitHub push or Jenkins job. Doing so frees up the development teams to restructure their build systems and validation as they desire because everything in the delivery process can remain the same as long as the Docker image makes it up to the repo.&lt;/p>
&lt;p>Sometimes you may need extra information if you&amp;rsquo;re triggering off of Docker images. For instance you might want to release anything on the master branch to production, but release any other branch to the staging area. In order to do so, put the extra information into the tag, and the pipeline triggers in Spinnaker can use regular expression matches on the tag name in Docker to determine which pipeline to execute.&lt;/p></description></item><item><title>Continuous-Deployment: Use the Spring Expression Language (SpEL) in Spinnaker Pipelines</title><link>/continuous-deployment/spinnaker-user-guides/expression-language/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/expression-language/</guid><description>
&lt;h2 id="overview-of-spel-in-spinnaker">Overview of SpEL in Spinnaker&lt;/h2>
&lt;p>The Spring Expression Language is a powerful tool that you can use to add logic and decision-making to your pipelines. While a lot of the time you will probably use it to evaluate variables, it can do a lot more. You can write straight Java/Groovy into it. This means you can do transformations, filters, maps, etc. You can use it to branch your pipeline into different directions.&lt;/p>
&lt;p>Some of the most common uses include:&lt;/p>
&lt;ul>
&lt;li>Getting build information from Jenkins&lt;/li>
&lt;li>Passing image names from one stage to another&lt;/li>
&lt;li>Retrieving a user&amp;rsquo;s manual judgement response&lt;/li>
&lt;/ul>
&lt;p>Take a look at the open source Spinnaker &lt;a href="https://spinnaker.io/docs/guides/user/pipeline/expressions/">Pipeline Expressions Guide&lt;/a> for a detailed overview of dynamically setting and accessing variables during pipeline execution.&lt;/p>
&lt;h2 id="common-techniques">Common techniques&lt;/h2>
&lt;h3 id="dynamically-defining-user-data">Dynamically defining User-Data&lt;/h3>
&lt;p>If you are creating a deployment configuration for AWS, Spinnaker gives you the option to provide &lt;a href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html#instancedata-add-user-data">user-data&lt;/a>. As you can see here:&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-05-26-at-11.20.47-AM.png"/>
&lt;/figure>
&lt;p>The user-data field needs to be base64 encoded. It is possible to create this dynamically with the built in expression language. To do this you can use the &lt;code>${ #toBase64() }&lt;/code> command. For example, You can pass the build number to the user-data via:&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-05-26-at-11.29.23-AM.png"/>
&lt;/figure>
&lt;h2 id="examples">Examples&lt;/h2>
&lt;p>You can find the expression language used in the examples within the &lt;a href="/continuous-deployment/spinnaker-user-guides/aws-guides/aws-baking-images/">baking images&lt;/a>, &lt;a href="/continuous-deployment/spinnaker-user-guides/aws-guides/aws-deploy/">deploying&lt;/a>, &lt;a href="/continuous-deployment/spinnaker-user-guides/working-with-jenkins/">working with Jenkins&lt;/a> and &lt;a href="/continuous-deployment/spinnaker-user-guides/aws-guides/aws-find-images/">finding images&lt;/a> guides.&lt;/p>
&lt;h2 id="troubleshooting">Troubleshooting&lt;/h2>
&lt;p>Sometimes using the expression language doesn&amp;rsquo;t go as anticipated. Here are some of the common issues:&lt;/p>
&lt;h3 id="autocomplete-popup-menu">Autocomplete popup menu&lt;/h3>
&lt;p>Sometimes the UI doesn&amp;rsquo;t display the autocomplete popup menu. This is because it doesn&amp;rsquo;t have the context to do so. Try filling in as much of the details as you can for the stages in your pipeline, then run the pipeline. After it has ran (it&amp;rsquo;s okay if it failed), go back to where you were trying to use the expression language and see if the autocomplete popup menu is displayed.&lt;/p>
&lt;h3 id="expression-doesnt-get-evaluated">Expression doesn&amp;rsquo;t get evaluated&lt;/h3>
&lt;p>Sometimes your expression is just printed out plainly and not evaluated. Usually this happens when the expression is invalid and/or can not be resolved and does not mean that Spinnaker isn&amp;rsquo;t trying to evaluate it. Try double checking that what you are referencing does exist. To check that it does exist, go to your pipeline&amp;rsquo;s execution details and click the &amp;lsquo;Source&amp;rsquo; link in the very bottom right hand corner.&lt;/p>
&lt;p>For example:
&lt;figure>
&lt;img src="/images/Image-2017-04-03-at-3.37.57-PM.png"/>
&lt;/figure>
&lt;/p>
&lt;p>You should see a page full of JSON. It doesn&amp;rsquo;t print in a very readable format, so you may want to copy and paste it into a text editor or another tool that will help you read it (I usually just curl this URL and pipe it to &lt;code>jq&lt;/code>). You can navigate to the JSON field &amp;lsquo;stages&amp;rsquo; for a list of stages in your pipeline. These stages are not necessarily in order. In the stage you&amp;rsquo;ll see another field called &amp;lsquo;context&amp;rsquo;. This is the information avaliable to the expression language. Make sure what you are referencing is in the context of the appropriate stage.&lt;/p>
&lt;h3 id="testing-your-pipeline-expressions">Testing your pipeline expressions&lt;/h3>
&lt;p>The best way to test a pipeline expression is to create a sample pipeline and see if your SPEL expression does what you expect it to do before you add it to your production pipeline. Another feature that is helpful for writing SPEL is if the pipeline has ran in the past, the UI will show you autocomplete options based on the previous execution.&lt;/p></description></item><item><title>Continuous-Deployment: Use Docker Images in Spinnaker</title><link>/continuous-deployment/spinnaker-user-guides/artifacts-docker-using/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/artifacts-docker-using/</guid><description>
&lt;h2 id="triggering-a-pipeline-with-a-docker-registry-update">Triggering a pipeline with a Docker Registry update&lt;/h2>
&lt;blockquote>
&lt;p>Before you start, you need to &lt;a href="/continuous-deployment/armory-admin/artifacts-docker-connect/">configure a Docker
registry&lt;/a>. If you don&amp;rsquo;t see your
Docker registry as an option, or you&amp;rsquo;re missing your organization or image in
the UI, you&amp;rsquo;ll need to double-check your Spinnaker is configured to use that
registry (and/or repository).&lt;/p>
&lt;/blockquote>
&lt;p>To add a Docker trigger to your pipeline, go to your configurations stage and
select &amp;ldquo;Add Trigger&amp;rdquo;, then select &amp;ldquo;Docker Registry&amp;rdquo; from the Type dropdown
menu. You should then be able to select the Registry to use, and find your
Organization(s) and Image(s).&lt;/p>
&lt;p>The Tag field is optional; if left empty, any new Docker image posted to the
registry (for that image) will trigger the pipeline. You can enter a regular
expression here to limit triggering to tag pattern names (for example,
&lt;code>master-.*&lt;/code> will only trigger when a new image with a tag starting with
&amp;ldquo;master-&amp;rdquo; is uploaded).&lt;/p>
&lt;blockquote>
&lt;p>A Regular Expression (&amp;ldquo;regex&amp;rdquo;) is similar to, but different than, a
wildcard. You may be familiar with wildcards on your command line,
where &lt;code>good*&lt;/code> would list all files that start with &amp;ldquo;good&amp;rdquo;.
In regexes, a * character simply &amp;ldquo;matches 0 or more of the preceding
character&amp;rdquo; &amp;ndash; &lt;code>good*&lt;/code> would then match &amp;ldquo;goo&amp;rdquo;, &amp;ldquo;good&amp;rdquo;, and
&amp;ldquo;goodddddd&amp;rdquo;, but wouldn&amp;rsquo;t match &amp;ldquo;goodbar&amp;rdquo;. If you need help with
the regex syntax, &lt;a href="https://en.wikipedia.org/wiki/Regular_expression">this&lt;/a>
is a good introduction.&lt;/p>
&lt;/blockquote>
&lt;figure>
&lt;img src="/images/docker-user-guide-1.gif"/>
&lt;/figure>
&lt;h2 id="referencing-the-new-image">Referencing the new image&lt;/h2>
&lt;p>When a new Docker image (that matches the Tag pattern, if set) is detected,
the Docker Registry trigger will fill in some context, which can be used
in &lt;a href="https://www.spinnaker.io/guides/user/pipeline/expressions/">SpEL Expressions&lt;/a>
elsewhere in the pipeline (for example, in a Kubernetes manifest).&lt;/p>
&lt;p>If you click on the &lt;code>Source&lt;/code> link for the pipeline that was triggered, you
can find the &lt;code>trigger&lt;/code> section of the JSON. Below is a partial block of
JSON as an example of the fields that will be filled in.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f1fa8c">&amp;#34;trigger&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">&amp;#34;account&amp;#34;&lt;/span>: &lt;span style="color:#f1fa8c">&amp;#34;my-docker-registry&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">&amp;#34;artifacts&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 style="color:#ff79c6">&amp;#34;name&amp;#34;&lt;/span>: &lt;span style="color:#f1fa8c">&amp;#34;index.docker.io/armory/demoapp&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">&amp;#34;reference&amp;#34;&lt;/span>: &lt;span style="color:#f1fa8c">&amp;#34;index.docker.io/armory/demoapp:master-29&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#f1fa8c">&amp;#34;docker/image&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">&amp;#34;version&amp;#34;&lt;/span>: &lt;span style="color:#f1fa8c">&amp;#34;master-29&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:#ff79c6">&amp;#34;repository&amp;#34;&lt;/span>: &lt;span style="color:#f1fa8c">&amp;#34;armory/demoapp&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">&amp;#34;tag&amp;#34;&lt;/span>: &lt;span style="color:#f1fa8c">&amp;#34;master-29&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">&amp;#34;type&amp;#34;&lt;/span>: &lt;span style="color:#f1fa8c">&amp;#34;docker&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>You can reference the Docker tag that triggered the pipeline with the
expression &lt;code>${trigger['tag']}&lt;/code>, which may be all you need, as in this
image spec line from a Kubernetes manifest:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>- image: &lt;span style="color:#f1fa8c">&amp;#34;docker.io/armory/demoapp:&lt;/span>&lt;span style="color:#f1fa8c">${&lt;/span>&lt;span style="color:#8be9fd;font-style:italic">trigger&lt;/span>[&lt;span style="color:#f1fa8c">&amp;#39;tag&amp;#39;&lt;/span>]&lt;span style="color:#f1fa8c">}&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Continuous-Deployment: Use GitHub in Spinnaker Pipelines</title><link>/continuous-deployment/spinnaker-user-guides/github/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/github/</guid><description>
&lt;h2 id="trigger-a-pipeline-with-a-github-commit">Trigger a Pipeline with a GitHub commit&lt;/h2>
&lt;blockquote>
&lt;p>Before you start, you&amp;rsquo;ll need to &lt;a href="/continuous-deployment/spinnaker-user-guides/artifacts-github-use/">configure your GitHub repositories&lt;/a>.
You&amp;rsquo;ll be able to configure a pipeline trigger without having configured
your GitHub webhook, but the trigger won&amp;rsquo;t fire until Spinnaker can receive
those calls from GitHub.&lt;/p>
&lt;/blockquote>
&lt;p>To add a GitHub trigger to your pipeline, go to your configurations stage
and select &amp;ldquo;Add Trigger&amp;rdquo;, then select &amp;ldquo;Git&amp;rdquo; from the Type dropdown menu.
Then select &amp;ldquo;github&amp;rdquo;. You can then enter your organization (ex. &amp;ldquo;armory&amp;rdquo;)
and the repository name to monitor (ex. &amp;ldquo;demoapp&amp;rdquo;). Branch and Secret
are optional, although it&amp;rsquo;s recommended you set Branch to whatever the name
of your production branch is (usually &lt;code>master&lt;/code>) so you only trigger pipelines
when code is committed to the production branch. The Branch field also
supports regular expressions, so you can limit the trigger to several branches
with common patterns or partial matches.&lt;/p>
&lt;blockquote>
&lt;p>A Regular Expression (&amp;ldquo;regex&amp;rdquo;) is similar to, but different than, a
wildcard. You may be familiar with wildcards on your command line,
where &lt;code>good*&lt;/code> would list all files that start with &amp;ldquo;good&amp;rdquo;.
In regexes, a * character simply &amp;ldquo;matches 0 or more of the preceding
character&amp;rdquo; &amp;ndash; &lt;code>good*&lt;/code> would then match &amp;ldquo;goo&amp;rdquo;, &amp;ldquo;good&amp;rdquo;, and
&amp;ldquo;goodddddd&amp;rdquo;, but wouldn&amp;rsquo;t match &amp;ldquo;goodbar&amp;rdquo;. If you need help with
the regex syntax, &lt;a href="https://en.wikipedia.org/wiki/Regular_expression">this&lt;/a>
is a good introduction.&lt;/p>
&lt;/blockquote>
&lt;figure>
&lt;img src="/images/github-user-guide-1.gif"/>
&lt;/figure></description></item><item><title>Continuous-Deployment: Use GitHub Artifacts in Spinnaker Pipelines</title><link>/continuous-deployment/spinnaker-user-guides/artifacts-github-use/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/artifacts-github-use/</guid><description>
&lt;h2 id="pulling-a-kubernetes-manifest-from-github">Pulling a Kubernetes Manifest from Github&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>Under &amp;ldquo;Expected Artifacts&amp;rdquo; in your pipeline, create an artifact of type &amp;ldquo;Github&amp;rdquo;.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Specify the &amp;ldquo;file path&amp;rdquo; as the path within the repository to your file. For example, if your manifest is at &lt;code>demo/manifests/deployment.yml&lt;/code> in the Github repository &lt;code>orgname/reponame&lt;/code> , specify &lt;code>demo/manifests/deployment.yml&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Check the &amp;ldquo;Use Default Artifact&amp;rdquo; checkbox.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>In the &amp;ldquo;Content URL&amp;rdquo;, provide the full path to the &lt;em>API URI&lt;/em> for your manifest. Here are some examples of this:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>If you&amp;rsquo;re using SaaS Github (&lt;a href="https://www.github.com">www.github.com&lt;/a>), the URI is generally formatted like this: &lt;code>https://api.github.com/repos/&amp;lt;ORG&amp;gt;/&amp;lt;REPO&amp;gt;/contents/&amp;lt;PATH-TO-FILE&amp;gt;&lt;/code>.&lt;/p>
&lt;ul>
&lt;li>For example: &lt;code>https://api.github.com/repos/armory/demo/contents/manifests/deployment.yml&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>If you have an on-prem Github Enterprise, then the URI may be formatted like this: &lt;code>https://&amp;lt;GITHUB_URL&amp;gt;/api/v3/repos/&amp;lt;ORG&amp;gt;/&amp;lt;REPO&amp;gt;/contents/&amp;lt;PATH-TO-FILE&amp;gt;&lt;/code>.&lt;/p>
&lt;ul>
&lt;li>For example: &lt;code>http://github.customername.com/api/v3/repos/armory/spinnaker-pipelines/contents/manifests/deployment.yml&lt;/code>&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Create a &amp;ldquo;Deploy (Manifest)&amp;rdquo; stage. Rather than specifying the manifest directly in the UI, under the &amp;ldquo;Manifest Source&amp;rdquo; specify &amp;ldquo;Artifact&amp;rdquo;, and in the &amp;ldquo;Expected Artifact&amp;rdquo; field, select the artifact you created above.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If you have multiple Github Accounts (credentials) added to your Spinnaker cluster, there should be a dropdown to select which one to use.&lt;/p>
&lt;/li>
&lt;/ol></description></item><item><title>Continuous-Deployment: Use Jenkins in Spinnaker</title><link>/continuous-deployment/spinnaker-user-guides/working-with-jenkins/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/working-with-jenkins/</guid><description>
&lt;h2 id="triggering-a-pipeline-with-jenkins">Triggering a pipeline with Jenkins&lt;/h2>
&lt;blockquote>
&lt;p>Before you start, you need to &lt;a href="/continuous-deployment/armory-admin/jenkins-connect/">configure
Jenkins&lt;/a>. If you don&amp;rsquo;t see
Jenkins as an option, or you&amp;rsquo;re not seeing the correct master/job combination
in the UI, you&amp;rsquo;ll need to double-check your Spinnaker is configured to use
that resource.&lt;/p>
&lt;/blockquote>
&lt;p>To add a Jenkins trigger to your pipeline, go to your configurations stage and select &amp;ldquo;add trigger&amp;rdquo;, then select &amp;ldquo;Jenkins&amp;rdquo; from the Type dropdown menu. Select a Master from the Master category list and then select a Job to trigger from the pipeline.&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-03-27-at-4.47.35-PM.png"/>
&lt;/figure>
&lt;p>Note: Make sure you archive your package files and your properties file in Jenkins.&lt;/p>
&lt;h3 id="property-file">Property File&lt;/h3>
&lt;p>The property file is a way to transfer information about your build from Jenkins to Spinnaker. The file needs to be archived by the Jenkins job and should contain key value pairs.&lt;/p>
&lt;p>As an example, if you had your Jenkins job create and archive a file named &lt;code>build.properties&lt;/code> which looks like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">COMMITER_NAME&lt;/span>&lt;span style="color:#ff79c6">=&lt;/span>andrew
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">BRANCH_NAME&lt;/span>&lt;span style="color:#ff79c6">=&lt;/span>mybranch
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">CONFIG&lt;/span>&lt;span style="color:#ff79c6">=&lt;/span>config-3059cad.tar.gz
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then in the property files field in the Spinnaker Jenkins trigger, fill it in with &lt;code>build.properties&lt;/code>.&lt;/p>
&lt;p>Now that those variables are in Spinnaker, we can access them elsewhere in our pipeline by using the built-in Spinnaker expression language.&lt;/p>
&lt;p>In any given stage we can use the expression &lt;code>${ trigger.properties['BRANCH_NAME']}&lt;/code> to access the property value of the variable named &lt;code>BRANCH_NAME&lt;/code>.&lt;/p>
&lt;p>Note: For more elaborate instructions on expression language, please refer to the &lt;a href="/continuous-deployment/spinnaker-user-guides/expression-language/">Spinnaker Expression Language Guide&lt;/a>.&lt;/p>
&lt;h2 id="how-to-trigger-a-jenkins-job-through-spinnaker">How to trigger a Jenkins job through Spinnaker&lt;/h2>
&lt;p>Step 1: In pipelines, click on &amp;lsquo;Add stage&amp;rsquo;.&lt;/p>
&lt;p>Step 2: Select &amp;lsquo;Jenkins&amp;rsquo; from Type. Name the Stage Name and Depends On if you need it.&lt;/p>
&lt;p>Step 3: Configure your Master and Job. If your Job is parameterized then Spinnaker will display a Parameters form for your input.&lt;/p>
&lt;p>Step 4: The Property File input works the same way as above - however you &lt;strong>cannot&lt;/strong> use the same expression above to access it. Instead, you would access it with &lt;code>${ #stage('Jenkins')['context']['BRANCH_NAME'] }&lt;/code>, supposing you wanted to access the &lt;code>BRANCH_NAME&lt;/code> variable.&lt;/p>
&lt;h3 id="example">Example&lt;/h3>
&lt;p>In this example, we will create a pipeline with a Jenkin&amp;rsquo;s job stage and a manual judgement that repeats back the value of a property in a properties file from the Jenkins&amp;rsquo; job.&lt;/p>
&lt;p>First we navigate to my Jenkins Master and create a new Jenkins 2.0 Pipeline Job. As you can see in the image below, this job creates and archives a file named &lt;code>build.properties&lt;/code> which contains the key value pair &lt;code>KEY=VAL&lt;/code>.&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-03-27-at-5.20.02-PM.png"/>
&lt;/figure>
&lt;p>Then we go back to Spinnaker and create a new pipeline. We will skip over the Jenkins trigger for this example and create a Jenkins stage. Make sure to name the stage &lt;code>Jenkins&lt;/code>. Make sure to type &lt;code>build.properties&lt;/code> into the Properties field so that Spinnaker know where to source the property values.&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-03-27-at-5.21.05-PM.png"/>
&lt;/figure>
&lt;p>To demonstrate accessing our properties file, let&amp;rsquo;s create a new &lt;code>Manual Judgement&lt;/code> stage. In the Instructions text box input:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">{&lt;/span> &lt;span style="color:#6272a4">#stage(&amp;#39;Jenkins&amp;#39;)[&amp;#39;context&amp;#39;][&amp;#39;KEY&amp;#39;] }&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Spinnaker has added the key value pairs from &lt;code>build.properties&lt;/code> in the Jenkins&amp;rsquo; job to the context of the Jenkins stage. The above expression allows us to access that information.&lt;/p>
&lt;p>Don&amp;rsquo;t forget to press the save button in the lower right corner. Your pipeline configuration should look like:&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-03-28-at-2.04.43-PM.png"/>
&lt;/figure>
&lt;p>Finally, navigate back to the pipeline execution screen an start a manual execution of the pipeline you just configured.&lt;/p>
&lt;p>When the execution gets to the manual judgement stage, you should see the word &lt;code>VAL&lt;/code> in the intructions text area.&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-03-28-at-2.06.45-PM.png"/>
&lt;/figure>
&lt;h2 id="running-scripts-on-spinnaker">Running scripts on Spinnaker&lt;/h2>
&lt;p>You can also run arbitrary scripts on Spinnaker (behind the scenes it is pushing this work off to a Jenkins master). Simply select the &lt;code>Script&lt;/code> type stage while configuring a pipeline. You should see a screen like:&lt;/p>
&lt;figure>
&lt;img src="/images/Image-2017-03-28-at-2.10.26-PM.png"/>
&lt;/figure></description></item><item><title>Continuous-Deployment: Use Kustomize for Manifest-Based Kubernetes Deployments in Spinnaker</title><link>/continuous-deployment/spinnaker-user-guides/kustomize-manifests/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/kustomize-manifests/</guid><description>
&lt;h2 id="overview-of-kustomize">Overview of Kustomize&lt;/h2>
&lt;p>Kustomize is a tool that lets you create customized Kubernetes deployments without modifying underlying YAML configuration files. Since the files remain unchanged, others are able to reuse the same files to build their own customizations. Your customizations are stored in a file called &lt;code>kustomization.yaml&lt;/code>. If you need to make configuration changes, the underlying YAML files and &lt;code>kustomization.yaml&lt;/code> can be updated independently of each other.
​
To learn more about Kustomize and how to define a &lt;code>kustomization.yaml&lt;/code> file, see the following links:
​&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/kubernetes-sigs/kustomize">Kubernetes SIG for Kustomize&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/kubernetes-sigs/kustomize/tree/master/docs">Documentation for Kustomize&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/kubernetes-sigs/kustomize/tree/master/examples/wordpress">Example Kustomization&lt;/a>
​&lt;/li>
&lt;/ul>
&lt;p>In the context of Spinnaker, Kustomize lets you generate a custom manifest, which can be deployed in a downstream &lt;code>Deploy (Manifest)&lt;/code> stage. This manifest is tailored to your requirements and built on existing configurations.
​
Spinnaker uses the latest non-kubectl version of Kustomize.
​&lt;/p>
&lt;h3 id="using-kustomize">Using Kustomize&lt;/h3>
&lt;p>​
Kustomize works by running &lt;code>kustomize build&lt;/code> against a &lt;code>kustomization.yaml&lt;/code> file located in a Git repository. This file defines all of the other files needed by Kustomize to render a fully hydrated manifest.
​&lt;/p>
&lt;h2 id="kustomize">Kustomize&lt;/h2>
&lt;h3 id="requirements">Requirements&lt;/h3>
&lt;p>Kustomize requires the &lt;code>git/repo&lt;/code> artifact type.&lt;/p>
&lt;p>&lt;strong>Configure git/repo artifact with Operator&lt;/strong>&lt;/p>
&lt;p>Add the following settings to the &lt;code>SpinnakerService&lt;/code> manifest:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-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:#ff79c6">apiVersion&lt;/span>: spinnaker.armory.io/v1alpha2
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">kind&lt;/span>: SpinnakerService
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">metadata&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">name&lt;/span>: spinnaker
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">spec&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">spinnakerConfig&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">config&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">artifacts&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">gitrepo&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">enabled&lt;/span>: &lt;span style="color:#ff79c6">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">accounts&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ff79c6">name&lt;/span>: gitrepo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">username&lt;/span>: &lt;span style="color:#6272a4"># Git username.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">token&lt;/span>: encrypted:k8s!n:spin-secrets!k:github-token &lt;span style="color:#6272a4"># Your github access token from a K8s secret (here secret=&amp;#39;spin-secrets&amp;#39;, key=&amp;#39;github-token&amp;#39;)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="build-the-pipeline">Build the Pipeline&lt;/h3>
&lt;p>​
For this example, you can use the &lt;em>helloWorld&lt;/em> example from the &lt;a href="https://github.com/kubernetes-sigs/kustomize">Kustomize public repository&lt;/a>.&lt;/p>
&lt;h3 id="step-1---add-an-expected-artifact">Step 1 - Add an Expected Artifact&lt;/h3>
&lt;p>​
Add a &lt;strong>git/repo&lt;/strong> Expected Artifact in the &lt;em>Configuration&lt;/em> section:
​&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Account&lt;/strong> (Required): The &lt;code>git/repo&lt;/code> account to use.&lt;/li>
&lt;li>&lt;strong>URL&lt;/strong> (Required): The location of the Git repository.&lt;/li>
&lt;li>&lt;strong>Branch&lt;/strong> (Optional): The branch of the repository you want to use. &lt;em>Defaults to &lt;code>master&lt;/code>.&lt;/em>&lt;/li>
&lt;li>&lt;strong>Subpath&lt;/strong> (Optional): By clicking &lt;code>Checkout subpath&lt;/code>, you can optionally pass in a relative subpath within the repository. This provides the option to checkout only a portion of the repository, thereby reducing the size of the generated artifact.
​
&lt;figure>
&lt;img src="/images/kustomize-expected-artifact.png"/>
&lt;/figure>
&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>In order to execute the pipeline manually, it is necessary to select &lt;strong>Use Default Artifact&lt;/strong> and also fill the fields (same information above).
​&lt;/p>
&lt;/blockquote>
&lt;h3 id="step-2---add-a-bake-manifest-stage">Step 2 - Add a Bake (Manifest) Stage&lt;/h3>
&lt;p>​
Add a &lt;strong>Bake (Manifest)&lt;/strong> stage and choose the Render Engine &lt;em>KUSTOMIZE&lt;/em>. Then, select the Expected Artifact you created in step 1 and specify the path for the &lt;strong>kustomization.yaml&lt;/strong> file.
​
&lt;figure>
&lt;img src="/images/kustomize-bake.png"/>
&lt;/figure>
​&lt;/p>
&lt;h3 id="step-3---produce-the-artifact">Step 3 - Produce the Artifact&lt;/h3>
&lt;p>​
Spinnaker returns the &lt;em>manifest&lt;/em> in a Base64 encoded file, so it is necessary to Produce a single Base64 Artifact in this Bake (Manifest) stage:
​
&lt;figure>
&lt;img src="/images/kustomize-base64.png"/>
&lt;/figure>
​&lt;/p>
&lt;h3 id="step-4---deploy">Step 4 - Deploy&lt;/h3>
&lt;p>​
Add a &lt;strong>Deploy (Manifest)&lt;/strong> stage. Make sure to select the &lt;em>Manifest Source&lt;/em>: &lt;strong>Artifact&lt;/strong> and select the Base64 Artifact produced by the &lt;em>Bake (Manifest)&lt;/em> stage.
​
&lt;figure>
&lt;img src="/images/kustomize-deploy.png"/>
&lt;/figure>
​&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Note:&lt;/strong> As we are deploying a manifest without a specified namespace, we need to override the namespace by checking the &lt;em>&amp;ldquo;Override Namespace&amp;rdquo;&lt;/em> option in the deployment stage.&lt;/p>
&lt;/blockquote>
&lt;h3 id="run-the-pipeline">Run the Pipeline&lt;/h3>
&lt;p>​
After you execute the pipeline, you can see the manifest generated in YAML format by clicking on the &lt;em>Baked Manifest YAML&lt;/em> link:
​
&lt;figure>
&lt;img src="/images/kustomize-execution.png"/>
&lt;/figure>
​&lt;/p></description></item><item><title>Continuous-Deployment: Use Max Concurrent Pipeline Executions</title><link>/continuous-deployment/spinnaker-user-guides/max-concurrent-pipeline-executions/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/max-concurrent-pipeline-executions/</guid><description>
&lt;h2 id="what-max-concurrent-pipeline-executions-does">What max concurrent pipeline executions does&lt;/h2>
&lt;p>Max concurrent pipeline executions allows you to throttle the number of maximum parallel pipeline executions, so you can configure your pipeline to maximize deployment frequency without encountering any performance degradations or timeouts. This effectively allows you to continue accelerating your deployment frequency and velocity.&lt;/p>
&lt;p>If max concurrent pipeline executions is enabled, pipelines queue when the max concurrent pipeline executions is reached. Any queued pipelines are allowed to run once the number of running pipeline executions drops below the max. If the max is set to 0, then pipelines do not queue.&lt;/p>
&lt;h2 id="how-to-enable-and-use-max-concurrent-pipeline-executions">How to enable and use max concurrent pipeline executions&lt;/h2>
&lt;p>You can find the max concurrent pipeline executions feature in the your pipeline’s &lt;strong>Configuration&lt;/strong> section. It is disabled by default. Uncheck the &lt;strong>Disable concurrent pipeline executions (only run one at a time).&lt;/strong> option. Then enter an integer in the &lt;strong>Maximum concurrent pipeline executions&lt;/strong> field and save your changes.&lt;/p></description></item><item><title>Continuous-Deployment: Use the Evaluate Artifacts Stage</title><link>/continuous-deployment/spinnaker-user-guides/evaluate-artifacts-stage-use/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/evaluate-artifacts-stage-use/</guid><description>
&lt;p>&lt;img src="/images/proprietary.svg" alt="Proprietary">&lt;/p>
&lt;h2 id="before-you-begin">Before you begin&lt;/h2>
&lt;p>The Evaluate Artifacts Stage is a plugin for Armory Continuous Deployment. You must have the plugin enabled. For more information, see &lt;a href="/continuous-deployment/armory-admin/evaluate-artifacts-stage-enable/">Enable the Evaluate Artifacts Stage Plugin&lt;/a>.&lt;/p>
&lt;h2 id="using-the-stage">Using the stage&lt;/h2>
&lt;p>At a high level, using this stage involves the following steps:&lt;/p>
&lt;ol>
&lt;li>In the Armory Continuous Deployment UI, navigate to the pipeline you want to modify.&lt;/li>
&lt;li>Add any parameters you might use within the pipeline.&lt;/li>
&lt;li>Add the stage called &lt;strong>Evaluate Artifacts&lt;/strong> stage to your pipeline.&lt;/li>
&lt;li>Under the &lt;strong>Evaluate Artifacts Configuration&lt;/strong>, select &lt;strong>Add Artifact&lt;/strong> to the stage. A window appears where you can enter your artifact definition.&lt;/li>
&lt;li>Enter your artifact definition:
&lt;ul>
&lt;li>Provide a descriptive name for the artifact.&lt;/li>
&lt;li>Enter the artifact definition in the &lt;strong>Contents&lt;/strong> section. When entering the definition, you can use a SpEL expression to parameterize it to use the input from step 2.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Save your changes.&lt;/li>
&lt;/ol>
&lt;p>When the pipeline runs, the UI prompts the user to enter a value for the parameter, and you can now reference this artifact in subsequent stages or other pipelines.&lt;/p>
&lt;h2 id="example">Example&lt;/h2>
&lt;p>This example combines Armory&amp;rsquo;s Terraform Integration and the Evaluate Artifact stage to insert values a user inputs into artifacts when the pipeline runs. The example uses the Evaluate Artifact stage to insert the app name, namespace and number of replicas into the sample Terraform script. Then, the Terraform Integration deploys the infrastructure (NGINX in this example).&lt;/p>
&lt;blockquote>
&lt;p>To follow along with this example, you need to have the &lt;a href="/continuous-deployment/armory-admin/terraform-enable-integration/">Terraform Integration stage enabled&lt;/a> and have permission to provision a Kubernetes cluster and any associated resources.&lt;/p>
&lt;/blockquote>
&lt;ol>
&lt;li>
&lt;p>In the Armory UI, create a new pipeline.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>For the &lt;strong>Configuration&lt;/strong> phase of the pipeline, add the following parameter:&lt;/p>
&lt;ul>
&lt;li>&lt;code>nameAndSpace&lt;/code>&lt;/li>
&lt;li>&lt;code>replicas&lt;/code> and set the &lt;strong>Default Value&lt;/strong> to &lt;code>2&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>For both parameters, set them to required. Optionally, pin the parameters.
&lt;figure>
&lt;img src="/images/eval-artifact-stage/example-add-parameters.jpg"
alt="Add the nameAndSpace and replicas parameters to the pipeline."/>
&lt;/figure>
&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Save your changes.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Add a stage to the pipeline and set the type to &lt;strong>Evaluate Artifacts&lt;/strong>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Provide a name for the stage.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Under &lt;strong>Evaluate Artifacts Configuration&lt;/strong>, add an artifact:&lt;/p>
&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>
&lt;p>This example creates a tfvar file, so name it &lt;code>testvariables.tfvar&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>For the &lt;strong>Contents&lt;/strong> section, insert the following snippet:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>namespace=&lt;span style="color:#f1fa8c">&amp;#34;${#readJson(parameters[&amp;#39;nameAndSpace&amp;#39;])[&amp;#39;space&amp;#39;]}&amp;#34;&lt;/span> # SpEL expression
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>deployName=&lt;span style="color:#f1fa8c">&amp;#34;${#readJson(parameters[&amp;#39;nameAndSpace&amp;#39;])[&amp;#39;name&amp;#39;]}&amp;#34;&lt;/span> # SpEL expression
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>replicas=${parameters.replicas}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;figure>
&lt;img src="/images/eval-artifact-stage/eval-artifact-add-spel-artifact.jpg"
alt="In the window that pops up, provide a name and add the sample JSON to the Contents section."/>
&lt;/figure>
&lt;p>All three fields were added previously as parameters for the pipeline. The &lt;code>namespace&lt;/code> and &lt;code>deployName&lt;/code> fields are SpEL expressions that evaluate JSON that you provide when the pipeline runs. &lt;code>replicas&lt;/code> accepts an integer value for the number of replicas you want. You can provide a value when the pipeline runs or use the default value of 2.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;ol>
&lt;li>
&lt;p>Create the artifact.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Wait until the &lt;strong>Save Changes&lt;/strong> button becomes available. Then, save your artifact.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Add a second artifact for &lt;strong>Evaluate Artifacts Configuration&lt;/strong>. Use the following example:&lt;/p>
&lt;p>Name the artifact &lt;code>main.tf&lt;/code> and use the following snippet for the &lt;strong>Contents&lt;/strong>:&lt;/p>
&lt;/li>
&lt;/ol>
&lt;details>
&lt;summary>Show the Terraform script that deploys an NGINX container in a Kubernetes cluster&lt;/summary>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-hcl" data-lang="hcl">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">variable&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;namespace&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> type &lt;span style="color:#ff79c6">=&lt;/span> &lt;span style="color:#ff79c6">string&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:#ff79c6">variable&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;deployName&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> type &lt;span style="color:#ff79c6">=&lt;/span> &lt;span style="color:#ff79c6">string&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:#ff79c6">variable&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;replicas&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> type &lt;span style="color:#ff79c6">=&lt;/span> &lt;span style="color:#ff79c6">number&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:#ff79c6">resource&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;kubernetes_namespace&amp;#34; &amp;#34;test&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">metadata&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name &lt;span style="color:#ff79c6">=&lt;/span> &lt;span style="color:#ff79c6">var&lt;/span>.&lt;span style="color:#ff79c6">namespace&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:#ff79c6">resource&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;kubernetes_deployment&amp;#34; &amp;#34;test&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">metadata&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name &lt;span style="color:#ff79c6">=&lt;/span> &lt;span style="color:#ff79c6">var&lt;/span>.&lt;span style="color:#ff79c6">deployName&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace &lt;span style="color:#ff79c6">=&lt;/span> &lt;span style="color:#ff79c6">kubernetes_namespace&lt;/span>.&lt;span style="color:#ff79c6">test&lt;/span>.&lt;span style="color:#ff79c6">metadata&lt;/span>.&lt;span style="color:#bd93f9">0&lt;/span>.&lt;span style="color:#ff79c6">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 style="color:#ff79c6">spec&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> replicas &lt;span style="color:#ff79c6">=&lt;/span> &lt;span style="color:#ff79c6">var&lt;/span>.&lt;span style="color:#ff79c6">replicas&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">selector&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> match_labels &lt;span style="color:#ff79c6">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app &lt;span style="color:#ff79c6">=&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;MyTestApp&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:#ff79c6">template&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">metadata&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> labels &lt;span style="color:#ff79c6">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> app &lt;span style="color:#ff79c6">=&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;MyTestApp&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:#ff79c6">spec&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">container&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> image &lt;span style="color:#ff79c6">=&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;nginx&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name &lt;span style="color:#ff79c6">=&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;nginx-container&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">port&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> container_port &lt;span style="color:#ff79c6">=&lt;/span> &lt;span style="color:#bd93f9">80&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> }
&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;/li>
&lt;/ol>
&lt;/details>
&lt;br>
&lt;p>Note that the &lt;code>variable&lt;/code> block at the beginning of the script declares the variables &lt;code>namespace&lt;/code>, &lt;code>deployName&lt;/code>, and &lt;code>replicas&lt;/code> for the script, which are configured as parameters for the pipeline.&lt;/p>
&lt;blockquote>
&lt;p>This example adds the Terraform script directly to the pipeline. You can also store it in source control, such as GitHub, and reference it that way.&lt;/p>
&lt;/blockquote>
&lt;ol start="10">
&lt;li>Add a Terraform stage to the pipeline with the following characteristics:&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>Set the &lt;strong>Action&lt;/strong> to &lt;strong>Plan&lt;/strong>.&lt;/li>
&lt;li>Select &lt;strong>Save Plan Output&lt;/strong>&lt;/li>
&lt;li>For &lt;strong>Main Terraform Artifact &amp;gt; Expected Artifact&lt;/strong>, select &lt;code>main.tf&lt;/code>. This is the Terraform script.&lt;/li>
&lt;li>For &lt;strong>Terraform Artifacts &amp;gt; Expected Artifact&lt;/strong>, select &lt;code>testvariables.tfVar&lt;/code>. This is the variables file for Terraform.&lt;/li>
&lt;li>For &lt;strong>Produces Artifacts&lt;/strong>, add an artifact named &lt;code>planfile&lt;/code> of type &lt;code>embedded artifact&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>The plan file is the result of the stage and can be consumed by Terraform stages that perform the apply action.&lt;/p>
&lt;ol start="11">
&lt;li>Optionally, add a Manual Judgment stage. Although not required, it is good practice to add a Manual Judgment after a plan stage so that you can confirm the stage does what you expect it to do.&lt;/li>
&lt;li>Add a second Terraform stage with the following characteristics:&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>Set the &lt;strong>Action&lt;/strong> to &lt;strong>Apply&lt;/strong>.&lt;/li>
&lt;li>For &lt;strong>Main Terraform Artifact &amp;gt; Expected Artifact&lt;/strong>, select &lt;code>main.tf&lt;/code>. This is the Terraform script.&lt;/li>
&lt;li>For &lt;strong>Terraform Artifacts &amp;gt; Expected Artifact&lt;/strong>, select &lt;code>planfile&lt;/code>. This is the artifact that the Terraform Plan stage produced.&lt;/li>
&lt;/ul>
&lt;ol start="13">
&lt;li>Save your changes. This is what the complete pipeline looks like:
&lt;figure>
&lt;img src="/images/eval-artifact-stage/eval-artifacts-full-pipeline.jpg"
alt="A view of the complete pipeline."/>
&lt;/figure>
&lt;/li>
&lt;li>Start a manual execution of the pipeline.&lt;/li>
&lt;li>The UI prompts you for values that get substitued into the pipeline:&lt;/li>
&lt;/ol>
&lt;figure>
&lt;img src="/images/eval-artifact-stage/eval-artifacts-execution-parameters.jpg"
alt="Provide the SpEL expression for the `nameAndSpace` parameter and an integer value for the `replicas` parameters."/>
&lt;/figure>
&lt;ul>
&lt;li>&lt;code>nameAndSpace: {&amp;quot;name&amp;quot;:&amp;quot;test-deployment&amp;quot;,&amp;quot;space&amp;quot;:&amp;quot;test-space-param&amp;quot;}&lt;/code> is a SpEL expression.&lt;/li>
&lt;li>&lt;code>replicas&lt;/code> is an integer value.&lt;/li>
&lt;/ul>
&lt;p>The pipeline creates values for variables in the Terraform script and the number of replicas. These are stored in a tfVar file.&lt;/p>
&lt;ol start="16">
&lt;li>Approve the Manual Judgment stage, which then starts the Terraform apply stage.&lt;/li>
&lt;/ol>
&lt;p>When the pipeline completes, you can query your Kubernetes cluster: &lt;code>kubectl get all -namespace test-space-param&lt;/code>. The command returns all the resources the Terraform stage created.&lt;/p>
&lt;h2 id="known-issues">Known issues&lt;/h2>
&lt;h3 id="problem-saving-artifacts">Problem saving artifacts&lt;/h3>
&lt;p>You may run into an issue where it seems like artifacts (or changes to them) are not being saved even though you click &lt;strong>Save Changes&lt;/strong>. This issue occurs because of how the UI handles updates to artifacts in relation to changes to other configurations.&lt;/p>
&lt;p>To avoid this issue, use the following workflow when you want to modify artifacts in a stage:&lt;/p>
&lt;ol>
&lt;li>Save any changes you have made to the pipeline before you modify artifacts.&lt;/li>
&lt;li>Make changes to the artifacts for the stage.&lt;/li>
&lt;li>Wait for the status in the bottom right of the UI to change from &lt;strong>In sync to the server&lt;/strong> to the action buttons.
This wait period is important. If you make other changes before the artifact is ready, the artifact will not be saved.&lt;/li>
&lt;li>Save your changes.&lt;/li>
&lt;li>Continue making other changes.&lt;/li>
&lt;/ol></description></item><item><title>Continuous-Deployment: Use Webhooks in Spinnaker Pipelines</title><link>/continuous-deployment/spinnaker-user-guides/webhooks/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/webhooks/</guid><description>
&lt;h2 id="how-does-spinnaker-use-webhooks">How does Spinnaker use webhooks?&lt;/h2>
&lt;p>Spinnaker uses &amp;ldquo;webhooks&amp;rdquo; in two ways &amp;ndash; as a trigger for pipeline execution
and as a stage that can make arbitrary calls to another service. If you&amp;rsquo;re
looking for information on configuring a webhook trigger that you can use
to run a pipeline, the open source community &lt;a href="https://www.spinnaker.io/guides/user/pipeline/triggers/webhooks/">has a very good guide for
that&lt;/a>.&lt;/p>
&lt;p>Spinnaker has a stage type called &amp;ldquo;Webhook&amp;rdquo; which allows the stage to call out to APIs as part of running a pipeline:&lt;/p>
&lt;figure>
&lt;img src="/images/webhook-type-selection.png"/>
&lt;/figure>
&lt;h2 id="setting-up-the-a-webhook-stage">Setting up the a webhook stage&lt;/h2>
&lt;p>The basic configuration is what you might expect. Fill in the URL to make the request against, the HTTP method to use, and depending on the request type the payload and/or additional headers:&lt;/p>
&lt;figure>
&lt;img src="/images/webhook-basic.png"/>
&lt;/figure>
&lt;p>Of particular note is that you can use &lt;a href="/continuous-deployment/spinnaker-user-guides/expression-language/">the Spinnaker pipeline expression language&lt;/a> both as part of the URL field and within the payload, making it easy to pass anything that&amp;rsquo;s available as part of the pipeline context.&lt;/p>
&lt;p>In this simple configuration the stage will be marked as successful if it gets a 2XX status code back, and will fail on anything else. If the return value from the request itself isn&amp;rsquo;t enough to determine the overall success you can check the &amp;ldquo;Wait for completion&amp;rdquo; checkbox and get a set of additional configuration:&lt;/p>
&lt;h2 id="wait-for-completion-using-status-field">Wait for completion using status field&lt;/h2>
&lt;figure>
&lt;img src="/images/webhook-completion.png"/>
&lt;/figure>
&lt;p>There are three different techniques Spinnaker can use to lookup the overall status:&lt;/p>
&lt;ul>
&lt;li>&amp;ldquo;GET method against webhook URL&amp;rdquo; means that once a request is sent using the method specified in the stage, Spinnaker will swap to polling the same webhook URL but using the GET method instead&lt;/li>
&lt;li>&amp;ldquo;From the Location header&amp;rdquo; means that Spinnaker will look for the URL to poll in the headers of the original request, and use that URL to poll for the final status&lt;/li>
&lt;li>&amp;ldquo;From webhook&amp;rsquo;s response&amp;rdquo; means Spinnaker will issue the original request, and will parse the response from that call to find a new URL it&amp;rsquo;ll use to poll for the final status&lt;/li>
&lt;/ul>
&lt;p>Spinnaker will use one of those mechanisms to find the status URL, and then repeatedly issue requests against that URL until it finds values that tell it what the final stage status should be. The &amp;ldquo;Status JsonPath&amp;rdquo; and mapping fields tell Spinnaker how to interpret the payloads that come back from the status URL. The &amp;ldquo;Status JsonPath&amp;rdquo; field is a &lt;a href="https://github.com/json-path/JsonPath">JsonPath&lt;/a> expression that Spinnaker uses to pull a single value from the payload for the status response. It compares the value from the payload to the values given in the SUCCESS, CANCELLED, and TERMINAL status mapping fields. Once there&amp;rsquo;s a match the overall state of the stage is set.&lt;/p>
&lt;h2 id="webhook-execution">Webhook execution&lt;/h2>
&lt;p>Spinnaker records the URL used as part of the webhook, the payload, and the status URL as part of the stage details. If the webhook transaction can run for a long time and there&amp;rsquo;s information available from the API, you can set the &amp;ldquo;Progress location&amp;rdquo; expression to also extract info to give some feedback about status in the Spinnaker UI. The &amp;ldquo;Progress location&amp;rdquo; value shows up in the Info field of the stage details:&lt;/p>
&lt;figure>
&lt;img src="/images/webhook-stage-details.png"/>
&lt;/figure>
&lt;p>Once the webhook stage is complete the payload is attached to the stage context as &amp;ldquo;buildInfo&amp;rdquo;. So if you need you can pull info out of the webhook response to pass into future stages using a pipeline expression. For instance, if the response from our stage above contains the value &amp;ldquo;threshold&amp;rdquo; that we want to use in another stage we can reference it like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">{&lt;/span> &lt;span style="color:#6272a4">#stage(&amp;#39;Webhook&amp;#39;)[&amp;#39;context&amp;#39;][&amp;#39;buildInfo&amp;#39;][&amp;#39;threshold&amp;#39;] }&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Continuous-Deployment: Spinnaker Video Tutorials</title><link>/continuous-deployment/spinnaker-user-guides/video-tutorials/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/continuous-deployment/spinnaker-user-guides/video-tutorials/</guid><description>
&lt;p>Spinnaker is the continuous delivery platform that codifies the software delivery best practices that put Netflix and Google a decade ahead of most other companies.&lt;/p>
&lt;h2 id="overview-tab">Overview tab&lt;/h2>
&lt;p>&lt;strong>What is Spinnaker&lt;/strong>&lt;/p>
&lt;div style="position: relative; padding-bottom: 35%; height: 0; overflow: hidden;">
&lt;iframe src="https://www.youtube-nocookie.com/embed/H_rFShgmJHY?rel=0&amp;start=0"
style="position: absolute; top: 0; left: 0; width: 75%; height: 75%; border: 0;"
allowfullscreen="" title="YouTube Video">&lt;/iframe>
&lt;/div>
&lt;p>&lt;strong>Concepts / Naming Conventions&lt;/strong>&lt;/p>
&lt;div style="position: relative; padding-bottom: 35%; height: 0; overflow: hidden;">
&lt;iframe src="https://www.youtube-nocookie.com/embed/b8N23gcdRHc?rel=0&amp;start=0"
style="position: absolute; top: 0; left: 0; width: 75%; height: 75%; border: 0;"
allowfullscreen="" title="YouTube Video">&lt;/iframe>
&lt;/div>
&lt;h2 id="how-to-create-a-pipeline">How To Create a Pipeline&lt;/h2>
&lt;div style="position: relative; padding-bottom: 35%; height: 0; overflow: hidden;">
&lt;iframe src="https://www.youtube-nocookie.com/embed/NBeUAjlcVJw?rel=0&amp;start=0"
style="position: absolute; top: 0; left: 0; width: 75%; height: 75%; border: 0;"
allowfullscreen="" title="YouTube Video">&lt;/iframe>
&lt;/div>
&lt;h2 id="how-to-build-a-pipeline">How to Build a Pipeline&lt;/h2>
&lt;div style="position: relative; padding-bottom: 35%; height: 0; overflow: hidden;">
&lt;iframe src="https://www.youtube-nocookie.com/embed/L8bJUFhcqGs?rel=0&amp;start=0"
style="position: absolute; top: 0; left: 0; width: 75%; height: 75%; border: 0;"
allowfullscreen="" title="YouTube Video">&lt;/iframe>
&lt;/div>
&lt;h2 id="various-pipeline-stages">Various Pipeline Stages&lt;/h2>
&lt;ul>
&lt;li>Bake Stage&lt;/li>
&lt;li>Deploy Stage&lt;/li>
&lt;li>Manual Judgement Stage&lt;/li>
&lt;li>Check Preconditions&lt;/li>
&lt;li>Adding a Parallel Stage&lt;/li>
&lt;/ul>
&lt;div style="position: relative; padding-bottom: 35%; height: 0; overflow: hidden;">
&lt;iframe src="https://www.youtube-nocookie.com/embed/dM1trF4rsqU?rel=0&amp;start=0"
style="position: absolute; top: 0; left: 0; width: 75%; height: 75%; border: 0;"
allowfullscreen="" title="YouTube Video">&lt;/iframe>
&lt;/div></description></item></channel></rss>