Repository: replicatedhq/kots
Pull request #1084 (2020-09-10 20:56:34)

IsAirgap, Sequence, Cursor, ChannelName, VersionLabel, ReleaseNotes


Patch:
diff --git a/kotsadm/go.mod b/kotsadm/go.mod
index db02775bc..a6ead0d4f 100644
--- a/kotsadm/go.mod
+++ b/kotsadm/go.mod
@@ -20,6 +20,7 @@ require (
 	github.com/gorilla/mux v1.7.4
 	github.com/gorilla/websocket v1.4.0
 	github.com/gosimple/slug v1.9.0
+	github.com/gotestyourself/gotestyourself v1.4.0 // indirect
 	github.com/lib/pq v1.3.0
 	github.com/marccampbell/yaml-toolbox v0.0.0-20200328202846-85b6f7184a20
 	github.com/mholt/archiver v3.1.1+incompatible
diff --git a/kotsadm/go.sum b/kotsadm/go.sum
index 6aa152e83..a102b2940 100644
--- a/kotsadm/go.sum
+++ b/kotsadm/go.sum
@@ -68,7 +68,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM=
 github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
-github.com/DataDog/zstd v1.4.0/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
 github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
 github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA=
 github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU=
@@ -231,19 +230,12 @@ github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDG
 github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
 github.com/containerd/typeurl v0.0.0-20190228175220-2a93cfde8c20/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
 github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
-github.com/containers/image/v5 v5.2.0 h1:DowY5OII5x9Pb6Pt76vnHU79BgG4/jdwhZjeAj2R+t8=
-github.com/containers/image/v5 v5.2.0/go.mod h1:IAub4gDGvXoxaIAdNy4e3FbVTDPVNMv9F0UfVVFbYCU=
 github.com/containers/image/v5 v5.5.2 h1:fv7FArz0zUnjH0W0l8t90CqWFlFcQrPP6Pug+9dUtVI=
 github.com/containers/image/v5 v5.5.2/go.mod h1:4PyNYR0nwlGq/ybVJD9hWlhmIsNra4Q8uOQX2s6E2uM=
 github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
 github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
-github.com/containers/ocicrypt v0.0.0-20190930154801-b87a4a69c741 h1:8tQkOcednLJtUcZgK7sPglscXtxvMOnFOa6wd09VWLM=
-github.com/containers/ocicrypt v0.0.0-20190930154801-b87a4a69c741/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
 github.com/containers/ocicrypt v1.0.2 h1:Q0/IPs8ohfbXNxEfyJ2pFVmvJu5BhqJUAmc6ES9NKbo=
 github.com/containers/ocicrypt v1.0.2/go.mod h1:nsOhbP19flrX6rE7ieGFvBlr7modwmNjsqWarIUce4M=
-github.com/containers/storage v1.15.8/go.mod h1:zhvjIIl/fR6wt/lgqQAC+xanHQ+8gUQ0GBVeXYN81qI=
-github.com/containers/storage v1.16.2 h1:S77Y+lmJcnGoPEZB2OOrTrRGyjT8viDCGyhVNNz78h8=
-github.com/containers/storage v1.16.2/go.mod h1:/RNmsK01ajCL+VtMSi3W8kHzpBwN+Q5gLYWgfw5wlMg=
 github.com/containers/storage v1.20.2 h1:tw/uKRPDnmVrluIzer3dawTFG/bTJLP8IEUyHFhltYk=
 github.com/containers/storage v1.20.2/go.mod h1:oOB9Ie8OVPojvoaKWEGSEtHbXUAs+tSyr7RO7ZGteMc=
 github.com/coredns/corefile-migration v1.0.6/go.mod h1:OFwBp/Wc9dJt5cAZzHWMNhK1r5L0p0jDwIBc6j8NC8E=
@@ -267,7 +259,6 @@ github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfc
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
@@ -295,7 +286,6 @@ github.com/docker/docker v0.0.0-20180522102801-da99009bbb11 h1:p8hSDXZgVhyh/C9bP
 github.com/docker/docker v0.0.0-20180522102801-da99009bbb11/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
 github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
-github.com/docker/go-connections v0.0.0-20180212134524-7beb39f0b969/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
 github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
@@ -331,8 +321,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM=
-github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
 github.com/euank/go-kmsg-parser v2.0.0+incompatible/go.mod h1:MhmAMZ8V4CYH4ybgdRwPr2TU5ThnS43puaKEMpja1uw=
 github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
@@ -355,7 +343,6 @@ github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXt
 github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc=
 github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
 github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/ghodss/yaml v0.0.0-20161207003320-04f313413ffd/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
@@ -570,7 +557,6 @@ github.com/gordonklaus/ineffassign v0.0.0-20180909121442-1003c8bd00dc/go.mod h1:
 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
 github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
-github.com/gorilla/mux v0.0.0-20170217192616-94e7d24fd285/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
 github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
@@ -583,8 +569,8 @@ github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.m
 github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
 github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY=
 github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo=
-github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI=
-github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
+github.com/gotestyourself/gotestyourself v1.4.0 h1:CDSlSIuRL/Fsc72Ln5lMybtrCvSRDddsHsDRG/nP7Rg=
+github.com/gotestyourself/gotestyourself v1.4.0/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY=
 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@@ -675,17 +661,11 @@ github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7IL
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
 github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.10.2 h1:Znfn6hXZAHaLPNnlqUYRrBSReFHYybslgv4PTiyz6P0=
-github.com/klauspost/compress v1.10.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/compress v1.10.8 h1:eLeJ3dr/Y9+XRfJT4l+8ZjmtB5RPJhucH2HeCV5+IZY=
 github.com/klauspost/compress v1.10.8/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
-github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
-github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM=
-github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
 github.com/klauspost/pgzip v1.2.4 h1:TQ7CNpYKovDOmqzRHKxJh0BeaBI7UdQZYc6p7pMQh1A=
 github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
@@ -770,7 +750,6 @@ github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
 github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-shellwords v1.0.5/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
-github.com/mattn/go-shellwords v1.0.9/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
 github.com/mattn/go-shellwords v1.0.10 h1:Y7Xqm8piKOO3v10Thp7Z36h4FYFjt5xB//6XvOrs2Gw=
 github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
 github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@@ -822,7 +801,6 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P
 github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
 github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk=
 github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
-github.com/mtrmac/gpgme v0.1.1/go.mod h1:GYYHnGSuS7HK3zVS2n3y73y0okK/BeKzwnn5jgiVFNI=
 github.com/mtrmac/gpgme v0.1.2 h1:dNOmvYmsrakgW7LcgiprD0yfRuQQe8/C8F6Z+zogO3s=
 github.com/mtrmac/gpgme v0.1.2/go.mod h1:GYYHnGSuS7HK3zVS2n3y73y0okK/BeKzwnn5jgiVFNI=
 github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
@@ -879,8 +857,6 @@ github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6/go.mod
 github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
 github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
 github.com/opencontainers/runc v1.0.0-rc10/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
-github.com/opencontainers/runc v1.0.0-rc9 h1:/k06BMULKF5hidyoZymkoDCzdJzltZpz/UU4LguQVtc=
-github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
 github.com/opencontainers/runc v1.0.0-rc90 h1:4+xo8mtWixbHoEm451+WJNUrq12o2/tDsyK9Vgc/NcA=
 github.com/opencontainers/runc v1.0.0-rc90/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
 github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
@@ -888,9 +864,6 @@ github.com/opencontainers/runtime-spec v1.0.0 h1:O6L965K88AilqnxeYPks/75HLpp4IG+
 github.com/opencontainers/runtime-spec v1.0.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
 github.com/opencontainers/selinux v1.3.1-0.20190929122143-5215b1806f52/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
-github.com/opencontainers/selinux v1.3.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
-github.com/opencontainers/selinux v1.3.3 h1:RX0wAeqtvVSYQcr017X3pFXPkLEtB6V4NjRD7gVQgg4=
-github.com/opencontainers/selinux v1.3.3/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
 github.com/opencontainers/selinux v1.5.2 h1:F6DgIsjgBIcDksLW4D5RG9bXok6oqZ3nvMwj4ZoFu/Q=
 github.com/opencontainers/selinux v1.5.2/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
 github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7lZWlQw5UXuoo=
@@ -1131,7 +1104,6 @@ github.com/undefinedlabs/go-mpatch v0.0.0-20200326085307-1a86426f42a6 h1:VO1oVFj
 github.com/undefinedlabs/go-mpatch v0.0.0-20200326085307-1a86426f42a6/go.mod h1:TyJZDQ/5AgyN7FSLiBJ8RO9u2c6wbtRvK827b6AVqY4=
 github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
 github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
-github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
 github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
@@ -1141,8 +1113,6 @@ github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOV
 github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
 github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE=
 github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g=
-github.com/vbauerster/mpb/v4 v4.11.2 h1:ynkUoKzi65DZ1UsQPx7sgi/KN6G9f7br+Us2nKm35AM=
-github.com/vbauerster/mpb/v4 v4.11.2/go.mod h1:jIuIRCltGJUnm6DCyPVkwjlLUk4nHTH+m4eD14CdFF0=
 github.com/vbauerster/mpb/v5 v5.2.2 h1:zIICVOm+XD+uV6crpSORaL6I0Q1WqOdvxZTp+r3L9cw=
 github.com/vbauerster/mpb/v5 v5.2.2/go.mod h1:W5Fvgw4dm3/0NhqzV8j6EacfuTe5SvnzBRwiXxDR9ww=
 github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
@@ -1160,7 +1130,6 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20190809123943-df4f5c81cb3b/go.mod h1:N2
 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
 github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
-github.com/xeipuuv/gojsonschema v0.0.0-20190816131739-be0936907f66/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
 github.com/xeipuuv/gojsonschema v1.1.0 h1:ngVtJC9TY/lg0AA/1k48FYhBrhRoFlEmWzsehpNAaZg=
 github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
 github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
@@ -1234,7 +1203,6 @@ golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8U
 golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
-golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -1294,7 +1262,6 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -1367,9 +1334,7 @@ golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191105231009-c1f44814a5cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/kotsadm/pkg/airgap/airgap.go b/kotsadm/pkg/airgap/airgap.go
index e4160b397..2ea616ba4 100644
--- a/kotsadm/pkg/airgap/airgap.go
+++ b/kotsadm/pkg/airgap/airgap.go
@@ -196,7 +196,7 @@ func CreateAppFromAirgap(pendingApp *types.PendingApp, airgapBundlePath string,
 		return errors.Wrap(err, "failed to create new version")
 	}
 
-	if err := preflight.Run(pendingApp.ID, newSequence, tmpRoot); err != nil {
+	if err := preflight.Run(pendingApp.ID, newSequence, true, tmpRoot); err != nil {
 		return errors.Wrap(err, "failed to start preflights")
 	}
 
diff --git a/kotsadm/pkg/airgap/update.go b/kotsadm/pkg/airgap/update.go
index 56e489216..b595cfbb6 100644
--- a/kotsadm/pkg/airgap/update.go
+++ b/kotsadm/pkg/airgap/update.go
@@ -11,13 +11,13 @@ import (
 
 	"github.com/pkg/errors"
 	apptypes "github.com/replicatedhq/kots/kotsadm/pkg/app/types"
-	"github.com/replicatedhq/kots/pkg/kotsutil"
 	"github.com/replicatedhq/kots/kotsadm/pkg/logger"
 	"github.com/replicatedhq/kots/kotsadm/pkg/preflight"
 	"github.com/replicatedhq/kots/kotsadm/pkg/store"
 	"github.com/replicatedhq/kots/kotsadm/pkg/version"
 	"github.com/replicatedhq/kots/pkg/crypto"
 	"github.com/replicatedhq/kots/pkg/cursor"
+	"github.com/replicatedhq/kots/pkg/kotsutil"
 	"github.com/replicatedhq/kots/pkg/pull"
 )
 
@@ -187,7 +187,7 @@ func UpdateAppFromAirgap(a *apptypes.App, airgapBundlePath string) (finalError e
 		return errors.Wrap(err, "failed to create new version")
 	}
 
-	if err := preflight.Run(a.ID, newSequence, currentArchivePath); err != nil {
+	if err := preflight.Run(a.ID, newSequence, true, currentArchivePath); err != nil {
 		return errors.Wrap(err, "failed to start preflights")
 	}
 
diff --git a/kotsadm/pkg/handlers/app.go b/kotsadm/pkg/handlers/app.go
index 0d0a668fd..3653e36f5 100644
--- a/kotsadm/pkg/handlers/app.go
+++ b/kotsadm/pkg/handlers/app.go
@@ -217,7 +217,7 @@ func responseAppFromApp(a *apptypes.App) (*ResponseApp, error) {
 			return nil, errors.Wrap(err, "failed to get current parent sequence for downstream")
 		}
 
-		s, err := store.GetStore().IsSnapshotsSupportedForVersion(a.ID, parentSequence)
+		s, err := store.GetStore().IsSnapshotsSupportedForVersion(a, parentSequence)
 		if err != nil {
 			return nil, errors.Wrap(err, "failed to check if snapshots is allowed")
 		}
diff --git a/kotsadm/pkg/handlers/config.go b/kotsadm/pkg/handlers/config.go
index 4af38c966..7a5b1559b 100644
--- a/kotsadm/pkg/handlers/config.go
+++ b/kotsadm/pkg/handlers/config.go
@@ -200,7 +200,8 @@ func LiveAppConfig(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
-	renderedConfig, err := kotsconfig.TemplateConfigObjects(kotsKinds.Config, configValues, appLicense, template.LocalRegistry{})
+	versionInfo := template.VersionInfoFromInstallation(liveAppConfigRequest.Sequence+1, foundApp.IsAirgap, kotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate)
+	renderedConfig, err := kotsconfig.TemplateConfigObjects(kotsKinds.Config, configValues, appLicense, template.LocalRegistry{}, &versionInfo)
 	if err != nil {
 		liveAppConfigResponse.Error = "failed to render templates"
 		JSON(w, http.StatusInternalServerError, liveAppConfigResponse)
@@ -265,7 +266,8 @@ func CurrentAppConfig(w http.ResponseWriter, r *http.Request) {
 		configValues[key] = generatedValue
 	}
 
-	renderedConfig, err := kotsconfig.TemplateConfigObjects(kotsKinds.Config, configValues, appLicense, template.LocalRegistry{})
+	versionInfo := template.VersionInfoFromInstallation(int64(sequence)+1, foundApp.IsAirgap, kotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate)
+	renderedConfig, err := kotsconfig.TemplateConfigObjects(kotsKinds.Config, configValues, appLicense, template.LocalRegistry{}, &versionInfo)
 	if err != nil {
 		currentAppConfigResponse.Error = "failed to render templates"
 		JSON(w, http.StatusInternalServerError, currentAppConfigResponse)
@@ -419,7 +421,7 @@ func updateAppConfig(updateApp *apptypes.App, sequence int64, req UpdateAppConfi
 		return updateAppConfigResponse, err
 	}
 
-	if err := preflight.Run(updateApp.ID, int64(sequence), archiveDir); err != nil {
+	if err := preflight.Run(updateApp.ID, int64(sequence), updateApp.IsAirgap, archiveDir); err != nil {
 		updateAppConfigResponse.Error = errors.Cause(err).Error()
 		return updateAppConfigResponse, err
 	}
diff --git a/kotsadm/pkg/handlers/preflight.go b/kotsadm/pkg/handlers/preflight.go
index 87d7d116d..28ff1c734 100644
--- a/kotsadm/pkg/handlers/preflight.go
+++ b/kotsadm/pkg/handlers/preflight.go
@@ -99,7 +99,7 @@ func IgnorePreflightRBACErrors(w http.ResponseWriter, r *http.Request) {
 
 	go func() {
 		defer os.RemoveAll(archiveDir)
-		if err := preflight.Run(foundApp.ID, int64(sequence), archiveDir); err != nil {
+		if err := preflight.Run(foundApp.ID, int64(sequence), foundApp.IsAirgap, archiveDir); err != nil {
 			logger.Error(err)
 			return
 		}
@@ -139,7 +139,7 @@ func StartPreflightChecks(w http.ResponseWriter, r *http.Request) {
 
 	go func() {
 		defer os.RemoveAll(archiveDir)
-		if err := preflight.Run(foundApp.ID, int64(sequence), archiveDir); err != nil {
+		if err := preflight.Run(foundApp.ID, int64(sequence), foundApp.IsAirgap, archiveDir); err != nil {
 			logger.Error(err)
 			return
 		}
@@ -230,7 +230,7 @@ func GetPreflightStatus(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	renderedPreflight, err := render.RenderFile(renderedKotsKinds, registrySettings, []byte(renderedMarshalledPreflights))
+	renderedPreflight, err := render.RenderFile(renderedKotsKinds, registrySettings, sequence, foundApp.IsAirgap, []byte(renderedMarshalledPreflights))
 	if err != nil {
 		err = errors.Wrap(err, "failed to render preflights")
 		logger.Error(err)
diff --git a/kotsadm/pkg/handlers/upload.go b/kotsadm/pkg/handlers/upload.go
index 17f5245d4..24186ea72 100644
--- a/kotsadm/pkg/handlers/upload.go
+++ b/kotsadm/pkg/handlers/upload.go
@@ -141,7 +141,7 @@ func UploadExistingApp(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	if err := preflight.Run(a.ID, newSequence, archiveDir); err != nil {
+	if err := preflight.Run(a.ID, newSequence, a.IsAirgap, archiveDir); err != nil {
 		logger.Error(err)
 		w.WriteHeader(500)
 		return
diff --git a/kotsadm/pkg/license/license.go b/kotsadm/pkg/license/license.go
index b7b001a4e..db88ae545 100644
--- a/kotsadm/pkg/license/license.go
+++ b/kotsadm/pkg/license/license.go
@@ -115,7 +115,7 @@ func createNewVersion(a *apptypes.App, archiveDir string, registrySettings *regi
 		return errors.Wrap(err, "failed to create new version")
 	}
 
-	if err := preflight.Run(a.ID, newSequence, archiveDir); err != nil {
+	if err := preflight.Run(a.ID, newSequence, a.IsAirgap, archiveDir); err != nil {
 		return errors.Wrap(err, "failed to run preflights")
 	}
 
diff --git a/kotsadm/pkg/online/online.go b/kotsadm/pkg/online/online.go
index bc2ab4785..7244bc351 100644
--- a/kotsadm/pkg/online/online.go
+++ b/kotsadm/pkg/online/online.go
@@ -193,7 +193,7 @@ func CreateAppFromOnline(pendingApp *types.PendingApp, upstreamURI string, isAut
 		}
 	}
 
-	if err := preflight.Run(pendingApp.ID, newSequence, tmpRoot); err != nil {
+	if err := preflight.Run(pendingApp.ID, newSequence, false, tmpRoot); err != nil {
 		return nil, errors.Wrap(err, "failed to start preflights")
 	}
 
diff --git a/kotsadm/pkg/preflight/preflight.go b/kotsadm/pkg/preflight/preflight.go
index 8b4e9b37b..bb67f128d 100644
--- a/kotsadm/pkg/preflight/preflight.go
+++ b/kotsadm/pkg/preflight/preflight.go
@@ -12,7 +12,7 @@ import (
 	"go.uber.org/zap"
 )
 
-func Run(appID string, sequence int64, archiveDir string) error {
+func Run(appID string, sequence int64, isAirgap bool, archiveDir string) error {
 	renderedKotsKinds, err := kotsutil.LoadKotsKindsFromPath(archiveDir)
 	if err != nil {
 		return errors.Wrap(err, "failed to load rendered kots kinds")
@@ -54,7 +54,7 @@ func Run(appID string, sequence int64, archiveDir string) error {
 			return errors.Wrap(err, "failed to get registry settings for app")
 		}
 
-		renderedPreflight, err := render.RenderFile(renderedKotsKinds, registrySettings, []byte(renderedMarshalledPreflights))
+		renderedPreflight, err := render.RenderFile(renderedKotsKinds, registrySettings, sequence, isAirgap, []byte(renderedMarshalledPreflights))
 		if err != nil {
 			return errors.Wrap(err, "failed to render preflights")
 		}
diff --git a/kotsadm/pkg/registry/registry.go b/kotsadm/pkg/registry/registry.go
index fd9dacaca..178deafe0 100644
--- a/kotsadm/pkg/registry/registry.go
+++ b/kotsadm/pkg/registry/registry.go
@@ -142,6 +142,7 @@ func RewriteImages(appID string, sequence int64, hostname string, username strin
 		RegistryNamespace: namespace,
 		AppSlug:           a.Slug,
 		IsGitOps:          a.IsGitOps,
+		AppSequence:       a.CurrentSequence + 1, // sequence +1 because this is the current latest sequence, not the sequence that the rendered version will be saved as
 	}
 
 	if err := rewrite.Rewrite(options); err != nil {
@@ -153,7 +154,7 @@ func RewriteImages(appID string, sequence int64, hostname string, username strin
 		return errors.Wrap(err, "failed to create new version")
 	}
 
-	if err := preflight.Run(appID, newSequence, appDir); err != nil {
+	if err := preflight.Run(appID, newSequence, a.IsAirgap, appDir); err != nil {
 		return errors.Wrap(err, "failed to run preflights")
 	}
 
diff --git a/kotsadm/pkg/render/render.go b/kotsadm/pkg/render/render.go
index a481ba6a2..b6fea7c05 100644
--- a/kotsadm/pkg/render/render.go
+++ b/kotsadm/pkg/render/render.go
@@ -9,28 +9,28 @@ import (
 	"github.com/pkg/errors"
 	apptypes "github.com/replicatedhq/kots/kotsadm/pkg/app/types"
 	downstreamtypes "github.com/replicatedhq/kots/kotsadm/pkg/downstream/types"
-	"github.com/replicatedhq/kots/pkg/kotsutil"
 	registrytypes "github.com/replicatedhq/kots/kotsadm/pkg/registry/types"
 	kotsv1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
 	"github.com/replicatedhq/kots/pkg/crypto"
+	"github.com/replicatedhq/kots/pkg/kotsutil"
 	"github.com/replicatedhq/kots/pkg/rewrite"
 	"github.com/replicatedhq/kots/pkg/template"
 )
 
 // RenderFile renders a single file
 // this is useful for upstream/kotskinds files that are not rendered in the dir
-func RenderFile(kotsKinds *kotsutil.KotsKinds, registrySettings *registrytypes.RegistrySettings, inputContent []byte) ([]byte, error) {
+func RenderFile(kotsKinds *kotsutil.KotsKinds, registrySettings *registrytypes.RegistrySettings, sequence int64, isAirgap bool, inputContent []byte) ([]byte, error) {
 	fixedUpContent, err := kotsutil.FixUpYAML(inputContent)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to fix up yaml")
 	}
 
-	return RenderContent(kotsKinds, registrySettings, fixedUpContent)
+	return RenderContent(kotsKinds, registrySettings, sequence, isAirgap, fixedUpContent)
 }
 
 // RenderContent renders any string/content
 // this is useful for rendering single values, like a status informer
-func RenderContent(kotsKinds *kotsutil.KotsKinds, registrySettings *registrytypes.RegistrySettings, inputContent []byte) ([]byte, error) {
+func RenderContent(kotsKinds *kotsutil.KotsKinds, registrySettings *registrytypes.RegistrySettings, sequence int64, isAirgap bool, inputContent []byte) ([]byte, error) {
 	apiCipher, err := crypto.AESCipherFromString(os.Getenv("API_ENCRYPTION_KEY"))
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to load apiCipher")
@@ -75,7 +75,8 @@ func RenderContent(kotsKinds *kotsutil.KotsKinds, registrySettings *registrytype
 		configGroups = kotsKinds.Config.Spec.Groups
 	}
 
-	builder, _, err := template.NewBuilder(configGroups, templateContextValues, localRegistry, appCipher, kotsKinds.License)
+	versionInfo := template.VersionInfoFromInstallation(sequence, isAirgap, kotsKinds.Installation.Spec)
+	builder, _, err := template.NewBuilder(configGroups, templateContextValues, localRegistry, appCipher, kotsKinds.License, &versionInfo)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to create builder")
 	}
@@ -132,6 +133,7 @@ func RenderDir(archiveDir string, a *apptypes.App, downstreams []downstreamtypes
 		IsAirgap:         a.IsAirgap,
 		AppSlug:          a.Slug,
 		IsGitOps:         a.IsGitOps,
+		AppSequence:      a.CurrentSequence + 1, // sequence +1 because this is the current latest sequence, not the sequence that the rendered version will be saved as
 	}
 
 	if registrySettings != nil {
diff --git a/kotsadm/pkg/snapshot/backup.go b/kotsadm/pkg/snapshot/backup.go
index c52f30dae..05b4c5dd4 100644
--- a/kotsadm/pkg/snapshot/backup.go
+++ b/kotsadm/pkg/snapshot/backup.go
@@ -87,7 +87,7 @@ func createApplicationBackup(ctx context.Context, a *apptypes.App, isScheduled b
 		return nil, errors.Wrap(err, "failed to get backup spec from kotskinds")
 	}
 
-	renderedBackup, err := render.RenderFile(kotsKinds, registrySettings, []byte(backupSpec))
+	renderedBackup, err := render.RenderFile(kotsKinds, registrySettings, a.CurrentSequence, a.IsAirgap, []byte(backupSpec))
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to render backup")
 	}
diff --git a/kotsadm/pkg/socketservice/socketservice.go b/kotsadm/pkg/socketservice/socketservice.go
index 354eadfc5..fb6f8846f 100644
--- a/kotsadm/pkg/socketservice/socketservice.go
+++ b/kotsadm/pkg/socketservice/socketservice.go
@@ -18,7 +18,6 @@ import (
 	appstatustypes "github.com/replicatedhq/kots/kotsadm/pkg/appstatus/types"
 	"github.com/replicatedhq/kots/kotsadm/pkg/downstream"
 	downstreamtypes "github.com/replicatedhq/kots/kotsadm/pkg/downstream/types"
-	"github.com/replicatedhq/kots/pkg/kotsutil"
 	"github.com/replicatedhq/kots/kotsadm/pkg/logger"
 	"github.com/replicatedhq/kots/kotsadm/pkg/render"
 	"github.com/replicatedhq/kots/kotsadm/pkg/snapshot"
@@ -28,6 +27,7 @@ import (
 	"github.com/replicatedhq/kots/kotsadm/pkg/supportbundle"
 	supportbundletypes "github.com/replicatedhq/kots/kotsadm/pkg/supportbundle/types"
 	"github.com/replicatedhq/kots/kotsadm/pkg/version"
+	"github.com/replicatedhq/kots/pkg/kotsutil"
 
 	velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
 )
@@ -291,7 +291,7 @@ func (s *SocketService) processDeploySocketForApp(clusterSocket ClusterSocket, a
 		// render status informers
 		renderedInformers := []string{}
 		for _, informer := range kotsKinds.KotsApplication.Spec.StatusInformers {
-			renderedInformer, err := render.RenderContent(kotsKinds, registrySettings, []byte(informer))
+			renderedInformer, err := render.RenderContent(kotsKinds, registrySettings, deployedVersion.Sequence, a.IsAirgap, []byte(informer))
 			if err != nil {
 				logger.Error(errors.Wrap(err, "failed to render status informer"))
 				continue
diff --git a/kotsadm/pkg/store/ocistore/version_store.go b/kotsadm/pkg/store/ocistore/version_store.go
index 2cfb17342..0290411be 100644
--- a/kotsadm/pkg/store/ocistore/version_store.go
+++ b/kotsadm/pkg/store/ocistore/version_store.go
@@ -18,9 +18,10 @@ import (
 	"github.com/mholt/archiver"
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 	"github.com/pkg/errors"
-	"github.com/replicatedhq/kots/pkg/kotsutil"
+	apptypes "github.com/replicatedhq/kots/kotsadm/pkg/app/types"
 	"github.com/replicatedhq/kots/kotsadm/pkg/logger"
 	versiontypes "github.com/replicatedhq/kots/kotsadm/pkg/version/types"
+	"github.com/replicatedhq/kots/pkg/kotsutil"
 	"go.uber.org/zap"
 )
 
@@ -131,7 +132,7 @@ func (s OCIStore) IsRollbackSupportedForVersion(appID string, sequence int64) (b
 	return appVersion.KOTSKinds.KotsApplication.Spec.AllowRollback, nil
 }
 
-func (s OCIStore) IsSnapshotsSupportedForVersion(appID string, sequence int64) (bool, error) {
+func (s OCIStore) IsSnapshotsSupportedForVersion(a *apptypes.App, sequence int64) (bool, error) {
 	return false, ErrNotImplemented
 }
 
diff --git a/kotsadm/pkg/store/s3pg/version_store.go b/kotsadm/pkg/store/s3pg/version_store.go
index 647b6de58..c7970c81a 100644
--- a/kotsadm/pkg/store/s3pg/version_store.go
+++ b/kotsadm/pkg/store/s3pg/version_store.go
@@ -15,6 +15,7 @@ import (
 	"github.com/aws/aws-sdk-go/service/s3/s3manager"
 	"github.com/mholt/archiver"
 	"github.com/pkg/errors"
+	apptypes "github.com/replicatedhq/kots/kotsadm/pkg/app/types"
 	"github.com/replicatedhq/kots/kotsadm/pkg/persistence"
 	"github.com/replicatedhq/kots/kotsadm/pkg/render"
 	kotss3 "github.com/replicatedhq/kots/kotsadm/pkg/s3"
@@ -90,10 +91,10 @@ func (s S3PGStore) IsRollbackSupportedForVersion(appID string, sequence int64) (
 	return kotsAppSpec.Spec.AllowRollback, nil
 }
 
-func (s S3PGStore) IsSnapshotsSupportedForVersion(appID string, sequence int64) (bool, error) {
+func (s S3PGStore) IsSnapshotsSupportedForVersion(a *apptypes.App, sequence int64) (bool, error) {
 	db := persistence.MustGetPGSession()
 	query := `select backup_spec from app_version where app_id = $1 and sequence = $2`
-	row := db.QueryRow(query, appID, sequence)
+	row := db.QueryRow(query, a.ID, sequence)
 
 	var backupSpecStr sql.NullString
 	if err := row.Scan(&backupSpecStr); err != nil {
@@ -107,7 +108,7 @@ func (s S3PGStore) IsSnapshotsSupportedForVersion(appID string, sequence int64)
 		return false, nil
 	}
 
-	archiveDir, err := s.GetAppVersionArchive(appID, sequence)
+	archiveDir, err := s.GetAppVersionArchive(a.ID, sequence)
 	if err != nil {
 		return false, errors.Wrap(err, "failed to get app version archive")
 	}
@@ -117,12 +118,12 @@ func (s S3PGStore) IsSnapshotsSupportedForVersion(appID string, sequence int64)
 		return false, errors.Wrap(err, "failed to load kots kinds from path")
 	}
 
-	registrySettings, err := s.GetRegistryDetailsForApp(appID)
+	registrySettings, err := s.GetRegistryDetailsForApp(a.ID)
 	if err != nil {
 		return false, errors.Wrap(err, "failed to get registry settings for app")
 	}
 
-	rendered, err := render.RenderFile(kotsKinds, registrySettings, []byte(backupSpecStr.String))
+	rendered, err := render.RenderFile(kotsKinds, registrySettings, sequence, a.IsAirgap, []byte(backupSpecStr.String))
 	if err != nil {
 		return false, errors.Wrap(err, "failed to render backup spec")
 	}
diff --git a/kotsadm/pkg/store/store_interface.go b/kotsadm/pkg/store/store_interface.go
index 90f52ef7b..79f7e56e7 100644
--- a/kotsadm/pkg/store/store_interface.go
+++ b/kotsadm/pkg/store/store_interface.go
@@ -127,7 +127,7 @@ type SnapshotStore interface {
 type VersionStore interface {
 	IsGitOpsSupportedForVersion(appID string, sequence int64) (bool, error)
 	IsRollbackSupportedForVersion(appID string, sequence int64) (bool, error)
-	IsSnapshotsSupportedForVersion(appID string, sequence int64) (bool, error)
+	IsSnapshotsSupportedForVersion(a *apptypes.App, sequence int64) (bool, error)
 	GetAppVersionArchive(appID string, sequence int64) (archivePath string, err error)
 	CreateAppVersionArchive(appID string, sequence int64, archivePath string) error
 	CreateAppVersion(string, *int64, string, string, *kotsutil.KotsKinds) (int64, error)
diff --git a/kotsadm/pkg/updatechecker/updatechecker.go b/kotsadm/pkg/updatechecker/updatechecker.go
index 99cf61edc..46af97b6d 100644
--- a/kotsadm/pkg/updatechecker/updatechecker.go
+++ b/kotsadm/pkg/updatechecker/updatechecker.go
@@ -9,12 +9,12 @@ import (
 	"github.com/pkg/errors"
 	"github.com/replicatedhq/kots/kotsadm/pkg/app"
 	"github.com/replicatedhq/kots/kotsadm/pkg/downstream"
-	"github.com/replicatedhq/kots/pkg/kotsutil"
 	"github.com/replicatedhq/kots/kotsadm/pkg/license"
 	"github.com/replicatedhq/kots/kotsadm/pkg/logger"
 	"github.com/replicatedhq/kots/kotsadm/pkg/store"
 	"github.com/replicatedhq/kots/kotsadm/pkg/upstream"
 	"github.com/replicatedhq/kots/kotsadm/pkg/version"
+	"github.com/replicatedhq/kots/pkg/kotsutil"
 	kotspull "github.com/replicatedhq/kots/pkg/pull"
 	cron "github.com/robfig/cron/v3"
 	"go.uber.org/zap"
diff --git a/kotsadm/pkg/upstream/upstream.go b/kotsadm/pkg/upstream/upstream.go
index 01fd2cced..3955cdbf2 100644
--- a/kotsadm/pkg/upstream/upstream.go
+++ b/kotsadm/pkg/upstream/upstream.go
@@ -10,12 +10,12 @@ import (
 	"time"
 
 	"github.com/pkg/errors"
-	"github.com/replicatedhq/kots/pkg/kotsutil"
 	"github.com/replicatedhq/kots/kotsadm/pkg/logger"
 	"github.com/replicatedhq/kots/kotsadm/pkg/preflight"
 	"github.com/replicatedhq/kots/kotsadm/pkg/store"
 	"github.com/replicatedhq/kots/kotsadm/pkg/version"
 	"github.com/replicatedhq/kots/pkg/crypto"
+	"github.com/replicatedhq/kots/pkg/kotsutil"
 	kotspull "github.com/replicatedhq/kots/pkg/pull"
 )
 
@@ -150,7 +150,7 @@ func DownloadUpdate(appID string, archiveDir string, toCursor string) (sequence
 		return 0, errors.Wrap(err, "failed to create version")
 	}
 
-	if err := preflight.Run(appID, newSequence, archiveDir); err != nil {
+	if err := preflight.Run(appID, newSequence, a.IsAirgap, archiveDir); err != nil {
 		return 0, errors.Wrap(err, "failed to run preflights")
 	}
 
diff --git a/kotsadm/pkg/version/version.go b/kotsadm/pkg/version/version.go
index 4d996aa80..440753610 100644
--- a/kotsadm/pkg/version/version.go
+++ b/kotsadm/pkg/version/version.go
@@ -10,12 +10,12 @@ import (
 	"github.com/replicatedhq/kots/kotsadm/pkg/config"
 	"github.com/replicatedhq/kots/kotsadm/pkg/downstream"
 	"github.com/replicatedhq/kots/kotsadm/pkg/gitops"
-	"github.com/replicatedhq/kots/pkg/kotsutil"
 	"github.com/replicatedhq/kots/kotsadm/pkg/persistence"
 	"github.com/replicatedhq/kots/kotsadm/pkg/secrets"
 	"github.com/replicatedhq/kots/kotsadm/pkg/store"
 	"github.com/replicatedhq/kots/kotsadm/pkg/version/types"
 	kotsv1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
+	"github.com/replicatedhq/kots/pkg/kotsutil"
 	"k8s.io/client-go/kubernetes/scheme"
 	applicationv1beta1 "sigs.k8s.io/application/api/v1beta1"
 )
diff --git a/pkg/base/render.go b/pkg/base/render.go
index 064f0f295..b1cec0f06 100644
--- a/pkg/base/render.go
+++ b/pkg/base/render.go
@@ -16,6 +16,8 @@ type RenderOptions struct {
 	LocalRegistryUsername  string
 	LocalRegistryPassword  string
 	ExcludeKotsKinds       bool
+	Sequence               int64
+	IsAirgap               bool
 	Log                    *logger.Logger
 }
 
diff --git a/pkg/base/replicated.go b/pkg/base/replicated.go
index 3b26cee9e..e87f11d7c 100644
--- a/pkg/base/replicated.go
+++ b/pkg/base/replicated.go
@@ -77,7 +77,16 @@ func renderReplicated(u *upstreamtypes.Upstream, renderOptions *RenderOptions) (
 		Password:  renderOptions.LocalRegistryPassword,
 	}
 
-	builder, _, err := template.NewBuilder(configGroups, templateContext, localRegistry, cipher, license)
+	versionInfo := template.VersionInfo{
+		Sequence:     renderOptions.Sequence,
+		Cursor:       u.UpdateCursor,
+		ChannelName:  u.ChannelName,
+		VersionLabel: u.VersionLabel,
+		ReleaseNotes: u.ReleaseNotes,
+		IsAirgap:     renderOptions.IsAirgap,
+	}
+
+	builder, _, err := template.NewBuilder(configGroups, templateContext, localRegistry, cipher, license, &versionInfo)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to create config context")
 	}
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 92604f94e..0605bb26b 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -19,8 +19,8 @@ func TemplateConfig(log *logger.Logger, configSpecData string, configValuesData
 	return templateConfig(log, configSpecData, configValuesData, licenseData, localRegistry, MarshalConfig)
 }
 
-func TemplateConfigObjects(configSpec *kotsv1beta1.Config, configValues map[string]template.ItemValue, license *kotsv1beta1.License, localRegistry template.LocalRegistry) (*kotsv1beta1.Config, error) {
-	templatedString, err := templateConfigObjects(configSpec, configValues, license, localRegistry, MarshalConfig)
+func TemplateConfigObjects(configSpec *kotsv1beta1.Config, configValues map[string]template.ItemValue, license *kotsv1beta1.License, localRegistry template.LocalRegistry, info *template.VersionInfo) (*kotsv1beta1.Config, error) {
+	templatedString, err := templateConfigObjects(configSpec, configValues, license, localRegistry, info, MarshalConfig)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to template config")
 	}
@@ -37,8 +37,8 @@ func TemplateConfigObjects(configSpec *kotsv1beta1.Config, configValues map[stri
 	return config, nil
 }
 
-func templateConfigObjects(configSpec *kotsv1beta1.Config, configValues map[string]template.ItemValue, license *kotsv1beta1.License, localRegistry template.LocalRegistry, marshalFunc func(config *kotsv1beta1.Config) (string, error)) (string, error) {
-	builder, configVals, err := template.NewBuilder(configSpec.Spec.Groups, configValues, localRegistry, nil, license)
+func templateConfigObjects(configSpec *kotsv1beta1.Config, configValues map[string]template.ItemValue, license *kotsv1beta1.License, localRegistry template.LocalRegistry, info *template.VersionInfo, marshalFunc func(config *kotsv1beta1.Config) (string, error)) (string, error) {
+	builder, configVals, err := template.NewBuilder(configSpec.Spec.Groups, configValues, localRegistry, nil, license, info)
 	if err != nil {
 		return "", errors.Wrap(err, "failed to create config context")
 	}
@@ -91,7 +91,7 @@ func templateConfig(log *logger.Logger, configSpecData string, configValuesData
 		templateContext = map[string]template.ItemValue{}
 	}
 
-	return templateConfigObjects(config, templateContext, license, localRegistry, marshalFunc)
+	return templateConfigObjects(config, templateContext, license, localRegistry, &template.VersionInfo{}, marshalFunc)
 }
 
 func ApplyValuesToConfig(config *kotsv1beta1.Config, values map[string]template.ItemValue) {
diff --git a/pkg/pull/pull.go b/pkg/pull/pull.go
index 73a754823..e11bda26a 100644
--- a/pkg/pull/pull.go
+++ b/pkg/pull/pull.go
@@ -114,6 +114,7 @@ func Pull(upstreamURI string, pullOptions PullOptions) (string, error) {
 	fetchOptions.UseAppDir = pullOptions.CreateAppDir
 	fetchOptions.LocalPath = pullOptions.LocalPath
 	fetchOptions.CurrentCursor = pullOptions.UpdateCursor
+	fetchOptions.AppSequence = pullOptions.AppSequence
 
 	var installation *kotsv1beta1.Installation
 
@@ -242,6 +243,8 @@ func Pull(upstreamURI string, pullOptions PullOptions) (string, error) {
 		LocalRegistryPassword:  pullOptions.RewriteImageOptions.Password,
 		ExcludeKotsKinds:       pullOptions.ExcludeKotsKinds,
 		Log:                    log,
+		Sequence:               pullOptions.AppSequence,
+		IsAirgap:               pullOptions.AirgapRoot != "",
 	}
 	log.ActionWithSpinner("Creating base")
 	io.WriteString(pullOptions.ReportWriter, "Creating base\n")
diff --git a/pkg/rewrite/rewrite.go b/pkg/rewrite/rewrite.go
index dfd06be68..9cbda7b1e 100644
--- a/pkg/rewrite/rewrite.go
+++ b/pkg/rewrite/rewrite.go
@@ -41,6 +41,7 @@ type RewriteOptions struct {
 	RegistryNamespace string
 	AppSlug           string
 	IsGitOps          bool
+	AppSequence       int64
 }
 
 func Rewrite(rewriteOptions RewriteOptions) error {
@@ -98,6 +99,8 @@ func Rewrite(rewriteOptions RewriteOptions) error {
 		LocalRegistryPassword:  rewriteOptions.RegistryPassword,
 		ExcludeKotsKinds:       rewriteOptions.ExcludeKotsKinds,
 		Log:                    log,
+		Sequence:               rewriteOptions.AppSequence,
+		IsAirgap:               rewriteOptions.IsAirgap,
 	}
 	log.ActionWithSpinner("Creating base")
 	io.WriteString(rewriteOptions.ReportWriter, "Creating base\n")
diff --git a/pkg/template/builder.go b/pkg/template/builder.go
index 1c503379f..5f648a14d 100644
--- a/pkg/template/builder.go
+++ b/pkg/template/builder.go
@@ -20,18 +20,19 @@ type Builder struct {
 	Functs template.FuncMap
 }
 
-// NewBuilder creates a builder with StaticCtx, LicenseCtx and ConfigCtx.
-func NewBuilder(configGroups []kotsv1beta1.ConfigGroup, existingValues map[string]ItemValue, localRegistry LocalRegistry, cipher *crypto.AESCipher, license *kotsv1beta1.License) (Builder, map[string]ItemValue, error) {
+// NewBuilder creates a builder with StaticCtx, licenseCtx and ConfigCtx.
+func NewBuilder(configGroups []kotsv1beta1.ConfigGroup, existingValues map[string]ItemValue, localRegistry LocalRegistry, cipher *crypto.AESCipher, license *kotsv1beta1.License, info *VersionInfo) (Builder, map[string]ItemValue, error) {
 	b := Builder{}
-	configCtx, err := b.newConfigContext(configGroups, existingValues, localRegistry, cipher, license)
+	configCtx, err := b.newConfigContext(configGroups, existingValues, localRegistry, cipher, license, info)
 	if err != nil {
 		return Builder{}, nil, errors.Wrap(err, "create config context")
 	}
 
 	b.Ctx = []Ctx{
 		StaticCtx{},
-		LicenseCtx{License: license},
-		NewKurlContext("base", "default"), // can be hardcoded because kurl always deploys to the default namespace
+		licenseCtx{License: license},
+		newKurlContext("base", "default"), // can be hardcoded because kurl always deploys to the default namespace
+		newVersionCtx(info),
 		configCtx,
 	}
 	return b, configCtx.ItemValues, nil
diff --git a/pkg/template/config_context.go b/pkg/template/config_context.go
index 41d8c4ec2..a47ebd442 100644
--- a/pkg/template/config_context.go
+++ b/pkg/template/config_context.go
@@ -69,7 +69,7 @@ type ConfigCtx struct {
 }
 
 // newConfigContext creates and returns a context for template rendering
-func (b *Builder) newConfigContext(configGroups []kotsv1beta1.ConfigGroup, existingValues map[string]ItemValue, localRegistry LocalRegistry, cipher *crypto.AESCipher, license *kotsv1beta1.License) (*ConfigCtx, error) {
+func (b *Builder) newConfigContext(configGroups []kotsv1beta1.ConfigGroup, existingValues map[string]ItemValue, localRegistry LocalRegistry, cipher *crypto.AESCipher, license *kotsv1beta1.License, info *VersionInfo) (*ConfigCtx, error) {
 	configCtx := &ConfigCtx{
 		ItemValues:    existingValues,
 		LocalRegistry: localRegistry,
@@ -80,8 +80,9 @@ func (b *Builder) newConfigContext(configGroups []kotsv1beta1.ConfigGroup, exist
 		Ctx: []Ctx{
 			configCtx,
 			StaticCtx{},
-			&LicenseCtx{License: license},
-			NewKurlContext("base", "default"),
+			&licenseCtx{License: license},
+			newKurlContext("base", "default"),
+			newVersionCtx(info),
 		},
 	}
 
diff --git a/pkg/template/kurl_context.go b/pkg/template/kurl_context.go
index 3f63eff1a..2d879aa5d 100644
--- a/pkg/template/kurl_context.go
+++ b/pkg/template/kurl_context.go
@@ -36,8 +36,8 @@ func GetKurlValues(installerName, nameSpace string) *kurlv1beta1.Installer {
 	return retrieved
 }
 
-func NewKurlContext(installerName, nameSpace string) *KurlCtx {
-	ctx := &KurlCtx{
+func newKurlContext(installerName, nameSpace string) *kurlCtx {
+	ctx := &kurlCtx{
 		KurlValues: make(map[string]interface{}),
 	}
 
@@ -50,7 +50,7 @@ func NewKurlContext(installerName, nameSpace string) *KurlCtx {
 	return ctx
 }
 
-func (ctx KurlCtx) AddValuesToKurlContext(retrieved *kurlv1beta1.Installer) {
+func (ctx kurlCtx) AddValuesToKurlContext(retrieved *kurlv1beta1.Installer) {
 	Spec := reflect.ValueOf(retrieved.Spec)
 
 	for i := 0; i < Spec.NumField(); i++ {
@@ -70,11 +70,11 @@ func (ctx KurlCtx) AddValuesToKurlContext(retrieved *kurlv1beta1.Installer) {
 	}
 }
 
-type KurlCtx struct {
+type kurlCtx struct {
 	KurlValues map[string]interface{}
 }
 
-func (ctx KurlCtx) FuncMap() template.FuncMap {
+func (ctx kurlCtx) FuncMap() template.FuncMap {
 	return template.FuncMap{
 		"KurlString": ctx.kurlString,
 		"KurlInt":    ctx.kurlInt,
@@ -84,7 +84,7 @@ func (ctx KurlCtx) FuncMap() template.FuncMap {
 	}
 }
 
-func (ctx KurlCtx) kurlBool(yamlPath string) bool {
+func (ctx kurlCtx) kurlBool(yamlPath string) bool {
 	if len(ctx.KurlValues) == 0 {
 		return false
 	}
@@ -104,7 +104,7 @@ func (ctx KurlCtx) kurlBool(yamlPath string) bool {
 	return b
 }
 
-func (ctx KurlCtx) kurlInt(yamlPath string) int {
+func (ctx kurlCtx) kurlInt(yamlPath string) int {
 	if len(ctx.KurlValues) == 0 {
 		return 0
 	}
@@ -124,7 +124,7 @@ func (ctx KurlCtx) kurlInt(yamlPath string) int {
 	return i
 }
 
-func (ctx KurlCtx) kurlString(yamlPath string) string {
+func (ctx kurlCtx) kurlString(yamlPath string) string {
 	if len(ctx.KurlValues) == 0 {
 		return ""
 	}
@@ -144,7 +144,7 @@ func (ctx KurlCtx) kurlString(yamlPath string) string {
 	return s
 }
 
-func (ctx KurlCtx) kurlOption(yamlPath string) string {
+func (ctx kurlCtx) kurlOption(yamlPath string) string {
 	if len(ctx.KurlValues) == 0 {
 		return ""
 	}
@@ -168,7 +168,7 @@ func (ctx KurlCtx) kurlOption(yamlPath string) string {
 	}
 }
 
-func (ctx KurlCtx) kurlAll() string {
+func (ctx kurlCtx) kurlAll() string {
 	//debug function to show all supported k:v pairs
 
 	keys := make([]string, len(ctx.KurlValues))
diff --git a/pkg/template/license_context.go b/pkg/template/license_context.go
index cc4d0d0a0..f3ea2f124 100644
--- a/pkg/template/license_context.go
+++ b/pkg/template/license_context.go
@@ -10,19 +10,19 @@ import (
 	kotsv1beta1 "github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
 )
 
-type LicenseCtx struct {
+type licenseCtx struct {
 	License *kotsv1beta1.License
 }
 
-// FuncMap represents the available functions in the LicenseCtx.
-func (ctx LicenseCtx) FuncMap() template.FuncMap {
+// FuncMap represents the available functions in the licenseCtx.
+func (ctx licenseCtx) FuncMap() template.FuncMap {
 	return template.FuncMap{
 		"LicenseFieldValue": ctx.licenseFieldValue,
 		"LicenseDockerCfg":  ctx.licenseDockercfg,
 	}
 }
 
-func (ctx LicenseCtx) licenseFieldValue(name string) string {
+func (ctx licenseCtx) licenseFieldValue(name string) string {
 	// return "" for a nil license - it's better than an error, which makes the template engine return "" for the full string
 	if ctx.License == nil {
 		return ""
@@ -58,7 +58,7 @@ func (ctx LicenseCtx) licenseFieldValue(name string) string {
 	}
 }
 
-func (ctx LicenseCtx) licenseDockercfg() string {
+func (ctx licenseCtx) licenseDockercfg() string {
 	// return "" for a nil license - it's better than an error, which makes the template engine return "" for the full string
 	if ctx.License == nil {
 		return ""
diff --git a/pkg/template/version_context.go b/pkg/template/version_context.go
new file mode 100644
index 000000000..2889304c8
--- /dev/null
+++ b/pkg/template/version_context.go
@@ -0,0 +1,89 @@
+package template
+
+import (
+	"text/template"
+
+	"github.com/replicatedhq/kots/kotskinds/apis/kots/v1beta1"
+)
+
+type VersionInfo struct {
+	Sequence     int64  // the installation sequence. Always 0 when being freshly installed, etc
+	Cursor       string // the upstream version cursor - integers for kots apps, may be semvers for helm charts
+	ChannelName  string // the name of the channel that the current version was from (kots apps only)
+	VersionLabel string // a pretty version label if provided (kots apps only)
+	ReleaseNotes string // the release notes for the given version (kots apps only)
+	IsAirgap     bool   // is this an airgap app (kots apps only)
+}
+
+type versionCtx struct {
+	info *VersionInfo
+}
+
+func newVersionCtx(info *VersionInfo) versionCtx {
+	return versionCtx{info: info}
+}
+
+func VersionInfoFromInstallation(sequence int64, isAirgap bool, spec v1beta1.InstallationSpec) VersionInfo {
+	return VersionInfo{
+		Sequence:     sequence,
+		Cursor:       spec.UpdateCursor,
+		ChannelName:  spec.ChannelName,
+		VersionLabel: spec.VersionLabel,
+		ReleaseNotes: spec.ReleaseNotes,
+		IsAirgap:     isAirgap,
+	}
+}
+
+// FuncMap represents the available functions in the versionCtx.
+func (ctx versionCtx) FuncMap() template.FuncMap {
+	return template.FuncMap{
+		"Sequence":     ctx.sequence,
+		"Cursor":       ctx.cursor,
+		"ChannelName":  ctx.channelName,
+		"VersionLabel": ctx.versionLabel,
+		"ReleaseNotes": ctx.releaseNotes,
+		"IsAirgap":     ctx.isAirgap,
+	}
+}
+
+func (ctx versionCtx) sequence() int64 {
+	if ctx.info == nil {
+		return -1
+	}
+	return ctx.info.Sequence
+}
+
+func (ctx versionCtx) cursor() string {
+	if ctx.info == nil {
+		return ""
+	}
+	return ctx.info.Cursor
+}
+
+func (ctx versionCtx) channelName() string {
+	if ctx.info == nil {
+		return ""
+	}
+	return ctx.info.ChannelName
+}
+
+func (ctx versionCtx) versionLabel() string {
+	if ctx.info == nil {
+		return ""
+	}
+	return ctx.info.VersionLabel
+}
+
+func (ctx versionCtx) releaseNotes() string {
+	if ctx.info == nil {
+		return ""
+	}
+	return ctx.info.ReleaseNotes
+}
+
+func (ctx versionCtx) isAirgap() bool {
+	if ctx.info == nil {
+		return false
+	}
+	return ctx.info.IsAirgap
+}
diff --git a/pkg/upstream/fetch.go b/pkg/upstream/fetch.go
index a90c3b375..535c7bebb 100644
--- a/pkg/upstream/fetch.go
+++ b/pkg/upstream/fetch.go
@@ -24,6 +24,7 @@ type FetchOptions struct {
 	CurrentCursor       string
 	CurrentChannel      string
 	CurrentVersionLabel string
+	AppSequence         int64
 }
 
 func FetchUpstream(upstreamURI string, fetchOptions *FetchOptions) (*types.Upstream, error) {
@@ -57,7 +58,7 @@ func downloadUpstream(upstreamURI string, fetchOptions *FetchOptions) (*types.Up
 		return downloadHelm(u, fetchOptions.HelmRepoURI)
 	}
 	if u.Scheme == "replicated" {
-		return downloadReplicated(u, fetchOptions.LocalPath, fetchOptions.RootDir, fetchOptions.UseAppDir, fetchOptions.License, fetchOptions.ConfigValues, pickCursor(fetchOptions), pickVersionLabel(fetchOptions), cipher)
+		return downloadReplicated(u, fetchOptions.LocalPath, fetchOptions.RootDir, fetchOptions.UseAppDir, fetchOptions.License, fetchOptions.ConfigValues, pickCursor(fetchOptions), pickVersionLabel(fetchOptions), cipher, fetchOptions.AppSequence, fetchOptions.Airgap != nil)
 	}
 	if u.Scheme == "git" {
 		return downloadGit(upstreamURI)
diff --git a/pkg/upstream/replicated.go b/pkg/upstream/replicated.go
index a1db31e61..a38887671 100644
--- a/pkg/upstream/replicated.go
+++ b/pkg/upstream/replicated.go
@@ -114,7 +114,7 @@ func getUpdatesReplicated(u *url.URL, localPath string, currentCursor Replicated
 	return updates, nil
 }
 
-func downloadReplicated(u *url.URL, localPath string, rootDir string, useAppDir bool, license *kotsv1beta1.License, existingConfigValues *kotsv1beta1.ConfigValues, updateCursor ReplicatedCursor, versionLabel string, cipher *crypto.AESCipher) (*types.Upstream, error) {
+func downloadReplicated(u *url.URL, localPath string, rootDir string, useAppDir bool, license *kotsv1beta1.License, existingConfigValues *kotsv1beta1.ConfigValues, updateCursor ReplicatedCursor, versionLabel string, cipher *crypto.AESCipher, appSequence int64, isAirgap bool) (*types.Upstream, error) {
 	var release *Release
 
 	if localPath != "" {
@@ -162,6 +162,12 @@ func downloadReplicated(u *url.URL, localPath string, rootDir string, useAppDir
 		release.ReleaseNotes = application.Spec.ReleaseNotes
 	}
 
+	// get channel name from license, if one was provided
+	channelName := ""
+	if license != nil {
+		channelName = license.Spec.ChannelName
+	}
+
 	if existingConfigValues == nil {
 		var prevConfigFile string
 		if useAppDir {
@@ -181,9 +187,18 @@ func downloadReplicated(u *url.URL, localPath string, rootDir string, useAppDir
 		return nil, errors.Wrap(err, "failed to find config in release")
 	}
 	if config != nil || existingConfigValues != nil {
+		versionInfo := template.VersionInfo{
+			Sequence:     appSequence,
+			Cursor:       updateCursor.Cursor,
+			ChannelName:  channelName,
+			VersionLabel: release.VersionLabel,
+			ReleaseNotes: release.ReleaseNotes,
+			IsAirgap:     isAirgap,
+		}
+
 		// If config existed and was removed from the app,
 		// values will be carried over to the new version anyway.
-		configValues, err := createConfigValues(application.Name, config, existingConfigValues, cipher, license)
+		configValues, err := createConfigValues(application.Name, config, existingConfigValues, cipher, license, &versionInfo)
 		if err != nil {
 			return nil, errors.Wrap(err, "failed to create empty config values")
 		}
@@ -201,12 +216,6 @@ func downloadReplicated(u *url.URL, localPath string, rootDir string, useAppDir
 		return nil, errors.Wrap(err, "failed to get files from release")
 	}
 
-	// get channel name from license, if one was provided
-	channelName := ""
-	if license != nil {
-		channelName = license.Spec.ChannelName
-	}
-
 	upstream := &types.Upstream{
 		URI:           u.RequestURI(),
 		Name:          application.Name,
@@ -487,7 +496,7 @@ func mustMarshalConfigValues(configValues *kotsv1beta1.ConfigValues) []byte {
 	return b.Bytes()
 }
 
-func createConfigValues(applicationName string, config *kotsv1beta1.Config, existingConfigValues *kotsv1beta1.ConfigValues, cipher *crypto.AESCipher, license *kotsv1beta1.License) (*kotsv1beta1.ConfigValues, error) {
+func createConfigValues(applicationName string, config *kotsv1beta1.Config, existingConfigValues *kotsv1beta1.ConfigValues, cipher *crypto.AESCipher, license *kotsv1beta1.License, versionInfo *template.VersionInfo) (*kotsv1beta1.ConfigValues, error) {
 	templateContextValues := make(map[string]template.ItemValue)
 
 	var newValues kotsv1beta1.ConfigValuesSpec
@@ -529,7 +538,7 @@ func createConfigValues(applicationName string, config *kotsv1beta1.Config, exis
 	// We should get this supported before 1.13.0 ships
 	localRegistry := template.LocalRegistry{}
 
-	builder, _, err := template.NewBuilder(config.Spec.Groups, templateContextValues, localRegistry, cipher, license)
+	builder, _, err := template.NewBuilder(config.Spec.Groups, templateContextValues, localRegistry, cipher, license, versionInfo)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to create config context")
 	}


Test patch:
diff --git a/pkg/template/config_context_test.go b/pkg/template/config_context_test.go
index e9738af54..bd00963b1 100644
--- a/pkg/template/config_context_test.go
+++ b/pkg/template/config_context_test.go
@@ -369,7 +369,7 @@ func TestBuilder_NewConfigContext(t *testing.T) {
 			builder.AddCtx(StaticCtx{})
 
 			localRegistry := LocalRegistry{}
-			got, err := builder.newConfigContext(tt.args.configGroups, tt.args.templateContext, localRegistry, tt.args.cipher, tt.args.license)
+			got, err := builder.newConfigContext(tt.args.configGroups, tt.args.templateContext, localRegistry, tt.args.cipher, tt.args.license, nil)
 			req.NoError(err)
 			req.Equal(tt.want, got)
 		})
diff --git a/pkg/template/kurl_context_test.go b/pkg/template/kurl_context_test.go
index 729f988a6..a3706bdb1 100644
--- a/pkg/template/kurl_context_test.go
+++ b/pkg/template/kurl_context_test.go
@@ -14,7 +14,7 @@ func TestBoolPresent(t *testing.T) {
 	defer scopetest.End()
 	req := require.New(t)
 
-	ctx := &KurlCtx{
+	ctx := &kurlCtx{
 		KurlValues: make(map[string]interface{}),
 	}
 
@@ -29,7 +29,7 @@ func TestBoolNotPresent(t *testing.T) {
 	defer scopetest.End()
 	req := require.New(t)
 
-	ctx := &KurlCtx{
+	ctx := &kurlCtx{
 		KurlValues: make(map[string]interface{}),
 	}
 
@@ -44,7 +44,7 @@ func TestBoolInvalidType(t *testing.T) {
 	defer scopetest.End()
 	req := require.New(t)
 
-	ctx := &KurlCtx{
+	ctx := &kurlCtx{
 		KurlValues: make(map[string]interface{}),
 	}
 
@@ -59,7 +59,7 @@ func TestStringPresent(t *testing.T) {
 	defer scopetest.End()
 	req := require.New(t)
 
-	ctx := &KurlCtx{
+	ctx := &kurlCtx{
 		KurlValues: make(map[string]interface{}),
 	}
 
@@ -74,7 +74,7 @@ func TestStringNotPresent(t *testing.T) {
 	defer scopetest.End()
 	req := require.New(t)
 
-	ctx := &KurlCtx{
+	ctx := &kurlCtx{
 		KurlValues: make(map[string]interface{}),
 	}
 
@@ -89,7 +89,7 @@ func TestStringInvalidType(t *testing.T) {
 	defer scopetest.End()
 	req := require.New(t)
 
-	ctx := &KurlCtx{
+	ctx := &kurlCtx{
 		KurlValues: make(map[string]interface{}),
 	}
 
@@ -104,7 +104,7 @@ func TestIntPresent(t *testing.T) {
 	defer scopetest.End()
 	req := require.New(t)
 
-	ctx := &KurlCtx{
+	ctx := &kurlCtx{
 		KurlValues: make(map[string]interface{}),
 	}
 
@@ -119,7 +119,7 @@ func TestIntNotPresent(t *testing.T) {
 	defer scopetest.End()
 	req := require.New(t)
 
-	ctx := &KurlCtx{
+	ctx := &kurlCtx{
 		KurlValues: make(map[string]interface{}),
 	}
 
@@ -134,7 +134,7 @@ func TestIntInvalidType(t *testing.T) {
 	defer scopetest.End()
 	req := require.New(t)
 
-	ctx := &KurlCtx{
+	ctx := &kurlCtx{
 		KurlValues: make(map[string]interface{}),
 	}
 
@@ -149,7 +149,7 @@ func TestOptionPresent(t *testing.T) {
 	defer scopetest.End()
 	req := require.New(t)
 
-	ctx := &KurlCtx{
+	ctx := &kurlCtx{
 		KurlValues: make(map[string]interface{}),
 	}
 
@@ -164,7 +164,7 @@ func TestOptionNotPresent(t *testing.T) {
 	defer scopetest.End()
 	req := require.New(t)
 
-	ctx := &KurlCtx{
+	ctx := &kurlCtx{
 		KurlValues: make(map[string]interface{}),
 	}
 
@@ -179,7 +179,7 @@ func TestKurlAll(t *testing.T) {
 	defer scopetest.End()
 	req := require.New(t)
 
-	ctx := &KurlCtx{
+	ctx := &kurlCtx{
 		KurlValues: make(map[string]interface{}),
 	}
 
@@ -196,7 +196,7 @@ func TestParseInstallerProperly(t *testing.T) {
 	defer scopetest.End()
 	req := require.New(t)
 
-	ctx := &KurlCtx{
+	ctx := &kurlCtx{
 		KurlValues: make(map[string]interface{}),
 	}
 
diff --git a/pkg/template/license_context_test.go b/pkg/template/license_context_test.go
index 45f6e9d71..d13d200df 100644
--- a/pkg/template/license_context_test.go
+++ b/pkg/template/license_context_test.go
@@ -13,7 +13,7 @@ func TestLicenseContext_dockercfg(t *testing.T) {
 	defer scopetest.End()
 	req := require.New(t)
 
-	ctx := LicenseCtx{
+	ctx := licenseCtx{
 		License: &kotsv1beta1.License{
 			Spec: kotsv1beta1.LicenseSpec{
 				LicenseID: "abcdef",
@@ -233,7 +233,7 @@ func TestLicenseCtx_licenseFieldValue(t *testing.T) {
 			defer scopetest.End()
 			req := require.New(t)
 
-			ctx := LicenseCtx{
+			ctx := licenseCtx{
 				License: tt.License,
 			}
 			req.Equal(tt.want, ctx.licenseFieldValue(tt.fieldName))
diff --git a/pkg/template/version_context_test.go b/pkg/template/version_context_test.go
new file mode 100644
index 000000000..e3159c6aa
--- /dev/null
+++ b/pkg/template/version_context_test.go
@@ -0,0 +1,47 @@
+package template
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+	"go.undefinedlabs.com/scopeagent"
+)
+
+func TestVersionContext(t *testing.T) {
+	scopetest := scopeagent.StartTest(t)
+	defer scopetest.End()
+	req := require.New(t)
+
+	// a properly populated versionCtx - should return the appropriate values
+	ctx := versionCtx{
+		info: &VersionInfo{
+			Sequence:     5,
+			Cursor:       "five",
+			ChannelName:  "chanFive",
+			VersionLabel: "verFive",
+			ReleaseNotes: "this is five",
+			IsAirgap:     true,
+		},
+	}
+
+	// an unpopulated versionCtx - should not error/panic
+	nilCtx := versionCtx{}
+
+	req.Equal(int64(5), ctx.sequence())
+	req.Equal(int64(-1), nilCtx.sequence())
+
+	req.Equal("five", ctx.cursor())
+	req.Equal("", nilCtx.cursor())
+
+	req.Equal("chanFive", ctx.channelName())
+	req.Equal("", nilCtx.channelName())
+
+	req.Equal("verFive", ctx.versionLabel())
+	req.Equal("", nilCtx.versionLabel())
+
+	req.Equal("this is five", ctx.releaseNotes())
+	req.Equal("", nilCtx.releaseNotes())
+
+	req.Equal(true, ctx.isAirgap())
+	req.Equal(false, nilCtx.isAirgap())
+}
diff --git a/pkg/upstream/replicated_test.go b/pkg/upstream/replicated_test.go
index 93d32d960..4602736a2 100644
--- a/pkg/upstream/replicated_test.go
+++ b/pkg/upstream/replicated_test.go
@@ -256,13 +256,13 @@ func Test_createConfigValues(t *testing.T) {
 			Default: "default_4",
 		},
 	}
-	values1, err := createConfigValues(applicationName, config, nil, nil, nil)
+	values1, err := createConfigValues(applicationName, config, nil, nil, nil, nil)
 	req.NoError(err)
 	assert.Equal(t, expected1, values1.Spec.Values)
 
 	// Like an app without a config, should have exact same values
 	expected2 := configValues.Spec.Values
-	values2, err := createConfigValues(applicationName, nil, configValues, nil, nil)
+	values2, err := createConfigValues(applicationName, nil, configValues, nil, nil, nil)
 	req.NoError(err)
 	assert.Equal(t, expected2, values2.Spec.Values)
 
@@ -283,7 +283,7 @@ func Test_createConfigValues(t *testing.T) {
 			Default: "default_4",
 		},
 	}
-	values3, err := createConfigValues(applicationName, config, configValues, nil, nil)
+	values3, err := createConfigValues(applicationName, config, configValues, nil, nil, nil)
 	req.NoError(err)
 	assert.Equal(t, expected3, values3.Spec.Values)
 }