Skip to content

Commit 990e3dd

Browse files
authored
Merge pull request #664 from fluxcd/embed-manifests
Use embedded Flux manifests for air-gapped bootstrap
2 parents ccdd032 + 7d9f8f0 commit 990e3dd

12 files changed

+174
-22
lines changed

.github/workflows/golangci-lint.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ jobs:
1616
with:
1717
go-version-file: 'go.mod'
1818
cache: true
19+
- name: Download Flux manifests
20+
run: make manifests
1921
- name: Run tidy
2022
run: make tidy
2123
- name: Check if working tree is dirty

.github/workflows/release.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ jobs:
3030
with:
3131
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
3232
passphrase: ${{ secrets.PASSPHRASE }}
33+
- name: Download Flux manifests
34+
run: make manifests
3335
- name: Run GoReleaser
3436
id: run-goreleaser
3537
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0

.github/workflows/tests.yaml

+11-8
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,8 @@ jobs:
9090
with:
9191
terraform_version: ${{ matrix.terraform }}
9292
terraform_wrapper: false
93-
- run: go mod download
94-
- env:
95-
TF_ACC: "1"
96-
run: go test -v -cover ./internal/provider/
97-
timeout-minutes: 10
93+
- name: Run tests
94+
run: make testacc
9895
e2e-flux-bootstrap-with-github:
9996
runs-on: ubuntu-latest
10097
needs: build
@@ -126,10 +123,12 @@ jobs:
126123
with:
127124
terraform_version: "${{env.TERRAFORM_VERSION}}"
128125
terraform_wrapper: false
129-
- name: Apply Terraform
126+
- name: Build provider
130127
run: |
131128
make build
132129
make terraformrc
130+
- name: Apply Terraform
131+
run: |
133132
export TF_CLI_CONFIG_FILE="${PWD}/.terraformrc"
134133
cd examples/github-via-ssh
135134
terraform init
@@ -216,10 +215,12 @@ jobs:
216215
with:
217216
terraform_version: "${{env.TERRAFORM_VERSION}}"
218217
terraform_wrapper: false
219-
- name: Apply Terraform
218+
- name: Build provider
220219
run: |
221220
make build
222221
make terraformrc
222+
- name: Apply Terraform
223+
run: |
223224
export TF_CLI_CONFIG_FILE="${PWD}/.terraformrc"
224225
cd examples/helm-install
225226
terraform init
@@ -279,10 +280,12 @@ jobs:
279280
with:
280281
terraform_version: "${{env.TERRAFORM_VERSION}}"
281282
terraform_wrapper: false
282-
- name: Apply Terraform
283+
- name: Build provider
283284
run: |
284285
make build
285286
make terraformrc
287+
- name: Apply Terraform
288+
run: |
286289
export TF_CLI_CONFIG_FILE="${PWD}/.terraformrc"
287290
cd examples/github-via-pat
288291
terraform init

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,8 @@ cover.out
3636

3737
# ignore .terraformrc for dev overrides
3838
.terraformrc
39+
40+
# ignore embedded manifests
41+
manifests/
42+
manifests.tar.gz
43+
.manifests.done

Makefile

+22-7
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,50 @@ SHELL := /bin/bash
44
TEST_COUNT?=1
55
ACCTEST_PARALLELISM?=5
66
ACCTEST_TIMEOUT?=10m
7+
EMBEDDED_MANIFESTS_TARGET=.manifests.done
8+
FLUX_VERSION:=$(shell grep 'DefaultFluxVersion' internal/utils/flux.go | awk '{ print $$5 }' | tr -d '"')
9+
10+
rwildcard=$(foreach d,$(wildcard $(addsuffix *,$(1))),$(call rwildcard,$(d)/,$(2)) $(filter $(subst *,%,$(2)),$(d)))
711

812
all: test testacc build
913

14+
$(EMBEDDED_MANIFESTS_TARGET): $(call rwildcard,manifests/,*.yaml)
15+
echo "Downloading manifests for Flux $(FLUX_VERSION)"
16+
rm -rf manifests && mkdir -p manifests
17+
curl -sLO https://github.com/fluxcd/flux2/releases/download/$(FLUX_VERSION)/manifests.tar.gz
18+
tar xzf manifests.tar.gz -C manifests
19+
rm -rf manifests.tar.gz
20+
touch $@
21+
22+
.PHONY: manifests
23+
manifests: $(EMBEDDED_MANIFESTS_TARGET)
24+
1025
tidy:
1126
rm -f go.sum; go mod tidy -compat=1.22
1227

1328
fmt:
1429
go fmt ./...
1530

16-
vet:
31+
vet: $(EMBEDDED_MANIFESTS_TARGET)
1732
go vet ./...
1833

19-
test: tidy fmt vet
34+
test: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet
2035
go test ./... -coverprofile cover.out
2136

22-
testacc: tidy fmt vet
23-
TF_ACC=1 go test ./... -v -count $(TEST_COUNT) -parallel $(ACCTEST_PARALLELISM) -timeout $(ACCTEST_TIMEOUT)
37+
testacc: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet
38+
TF_ACC=1 go test ./... -v -count $(TEST_COUNT) -parallel $(ACCTEST_PARALLELISM) -timeout $(ACCTEST_TIMEOUT) -coverprofile cover.out
2439

2540
# Run acceptance tests on macOS with the gitea-flux instance
2641
# Requires the following entry in /etc/hosts:
2742
# 127.0.0.1 gitea-flux
28-
testmacos: tidy fmt vet
43+
testmacos: $(EMBEDDED_MANIFESTS_TARGET) tidy fmt vet
2944
TF_ACC=1 GITEA_HOSTNAME=gitea-flux go test ./... -v -parallel 1 -run TestAccBootstrapGit_Drift
3045

31-
build:
46+
build: $(EMBEDDED_MANIFESTS_TARGET)
3247
CGO_ENABLED=0 go build -o ./bin/terraform-provider-flux main.go
3348

3449
.PHONY: docs
35-
docs: tools
50+
docs: $(EMBEDDED_MANIFESTS_TARGET) tools
3651
tfplugindocs generate --ignore-deprecated true
3752

3853
tools:

docs/resources/bootstrap_git.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,13 @@ The following examples are available to help you use the provider:
3232
- `components_extra` (Set of String) List of extra components to include in the install manifests.
3333
- `delete_git_manifests` (Boolean) Delete manifests from git repository. Defaults to `true`.
3434
- `disable_secret_creation` (Boolean) Use the existing secret for flux controller and don't create one from bootstrap
35+
- `embedded_manifests` (Boolean) When enabled, the Flux manifests will be extracted from the provider binary instead of being downloaded from github.com. Defaults to `false`.
3536
- `image_pull_secret` (String) Kubernetes secret name used for pulling the toolkit images from a private registry.
3637
- `interval` (String) Interval at which to reconcile from bootstrap repository. Defaults to `1m0s`.
3738
- `keep_namespace` (Boolean) Keep the namespace after uninstalling Flux components. Defaults to `false`.
3839
- `kustomization_override` (String) Kustomization to override configuration set by default.
3940
- `log_level` (String) Log level for toolkit components. Defaults to `info`.
40-
- `manifests_path` (String) The install manifests are built from a GitHub release or kustomize overlay if using a local path. Defaults to `https://github.com/fluxcd/flux2/releases`.
41+
- `manifests_path` (String, Deprecated) The install manifests are built from a GitHub release or kustomize overlay if using a local path. Defaults to `https://github.com/fluxcd/flux2/releases`.
4142
- `namespace` (String) The namespace scope for install manifests. Defaults to `flux-system`. It will be created if it does not exist.
4243
- `network_policy` (Boolean) Deny ingress access to the toolkit controllers from other namespaces using network policies. Defaults to `true`.
4344
- `path` (String) Path relative to the repository root, when specified the cluster sync will be scoped to this path (immutable).
@@ -46,7 +47,7 @@ The following examples are available to help you use the provider:
4647
- `secret_name` (String) Name of the secret the sync credentials can be found in or stored to. Defaults to `flux-system`.
4748
- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
4849
- `toleration_keys` (Set of String) List of toleration keys used to schedule the components pods onto nodes with matching taints.
49-
- `version` (String) Flux version. Defaults to `v2.2.3`.
50+
- `version` (String) Flux version. Defaults to `v2.2.3`. Has no effect when `embedded_manifests` is enabled.
5051
- `watch_all_namespaces` (Boolean) If true watch for custom resources in all namespaces. Defaults to `true`.
5152

5253
### Read-Only

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ require (
4141
github.com/hashicorp/terraform-plugin-sdk/v2 v2.27.0
4242
github.com/hashicorp/terraform-plugin-testing v1.3.0
4343
github.com/mitchellh/go-homedir v1.1.0
44+
github.com/otiai10/copy v1.14.0
4445
github.com/stretchr/testify v1.9.0
4546
k8s.io/api v0.29.3
4647
k8s.io/apiextensions-apiserver v0.29.3

go.sum

+4
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,10 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
365365
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
366366
github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8=
367367
github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
368+
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
369+
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
370+
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
371+
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
368372
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
369373
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
370374
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=

internal/provider/provider.go

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ const (
4040
defaultAuthor = "Flux"
4141
)
4242

43+
var EmbeddedManifests string
44+
4345
type Ssh struct {
4446
Username types.String `tfsdk:"username"`
4547
Password types.String `tfsdk:"password"`

internal/provider/resource_bootstrap_git.go

+25-4
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ type bootstrapGitResourceData struct {
9898
ComponentsExtra types.Set `tfsdk:"components_extra"`
9999
DeleteGitManifests types.Bool `tfsdk:"delete_git_manifests"`
100100
DisableSecretCreation types.Bool `tfsdk:"disable_secret_creation"`
101+
EmbeddedManifests types.Bool `tfsdk:"embedded_manifests"`
101102
ID types.String `tfsdk:"id"`
102103
ImagePullSecret types.String `tfsdk:"image_pull_secret"`
103104
Interval customtypes.Duration `tfsdk:"interval"`
@@ -199,6 +200,10 @@ func (r *bootstrapGitResource) Schema(ctx context.Context, req resource.SchemaRe
199200
Description: "Use the existing secret for flux controller and don't create one from bootstrap",
200201
Optional: true,
201202
},
203+
"embedded_manifests": schema.BoolAttribute{
204+
Description: "When enabled, the Flux manifests will be extracted from the provider binary instead of being downloaded from github.com. Defaults to `false`.",
205+
Optional: true,
206+
},
202207
"id": schema.StringAttribute{
203208
Computed: true,
204209
PlanModifiers: []planmodifier.String{
@@ -241,8 +246,9 @@ func (r *bootstrapGitResource) Schema(ctx context.Context, req resource.SchemaRe
241246
},
242247
},
243248
"manifests_path": schema.StringAttribute{
244-
Description: fmt.Sprintf("The install manifests are built from a GitHub release or kustomize overlay if using a local path. Defaults to `%s`.", defaultOpts.BaseURL),
245-
Optional: true,
249+
Description: fmt.Sprintf("The install manifests are built from a GitHub release or kustomize overlay if using a local path. Defaults to `%s`.", defaultOpts.BaseURL),
250+
Optional: true,
251+
DeprecationMessage: "This attribute is deprecated. Use the `embedded_manifests` attribute when running bootstrap on air-gapped environments.",
246252
},
247253
"namespace": schema.StringAttribute{
248254
Description: fmt.Sprintf("The namespace scope for install manifests. Defaults to `%s`. It will be created if it does not exist.", defaultOpts.Namespace),
@@ -309,7 +315,7 @@ func (r *bootstrapGitResource) Schema(ctx context.Context, req resource.SchemaRe
309315
},
310316
},
311317
"version": schema.StringAttribute{
312-
Description: fmt.Sprintf("Flux version. Defaults to `%s`.", utils.DefaultFluxVersion),
318+
Description: fmt.Sprintf("Flux version. Defaults to `%s`. Has no effect when `embedded_manifests` is enabled.", utils.DefaultFluxVersion),
313319
Optional: true,
314320
Computed: true,
315321
Default: stringdefault.StaticString(utils.DefaultFluxVersion),
@@ -446,6 +452,9 @@ func (r *bootstrapGitResource) Create(ctx context.Context, req resource.CreateRe
446452
}
447453

448454
manifestsBase := ""
455+
if data.EmbeddedManifests.ValueBool() {
456+
manifestsBase = EmbeddedManifests
457+
}
449458
err = bootstrap.Run(ctx, bootstrapProvider, manifestsBase, installOpts, secretOpts, syncOpts, 2*time.Second, timeout)
450459
if err != nil {
451460
resp.Diagnostics.AddError("Bootstrap run error", err.Error())
@@ -679,6 +688,9 @@ func (r bootstrapGitResource) Update(ctx context.Context, req resource.UpdateReq
679688
}
680689

681690
manifestsBase := ""
691+
if data.EmbeddedManifests.ValueBool() {
692+
manifestsBase = EmbeddedManifests
693+
}
682694
err = bootstrap.Run(ctx, bootstrapProvider, manifestsBase, installOpts, secretOpts, syncOpts, 2*time.Second, timeout)
683695
if err != nil {
684696
resp.Diagnostics.AddError("Bootstrap run error", err.Error())
@@ -1116,18 +1128,27 @@ func getSyncOptions(data bootstrapGitResourceData, url *url.URL, branch string)
11161128
func getExpectedRepositoryFiles(data bootstrapGitResourceData, url *url.URL, branch string) (map[string]string, error) {
11171129
repositoryFiles := map[string]string{}
11181130
installOpts := getInstallOptions(data)
1119-
installManifests, err := install.Generate(installOpts, "")
1131+
manifestsBase := ""
1132+
if data.EmbeddedManifests.ValueBool() {
1133+
manifestsBase = EmbeddedManifests
1134+
}
1135+
1136+
installManifests, err := install.Generate(installOpts, manifestsBase)
11201137
if err != nil {
11211138
return nil, fmt.Errorf("could not generate install manifests: %w", err)
11221139
}
1140+
11231141
repositoryFiles[installManifests.Path] = installManifests.Content
1142+
11241143
syncOpts := getSyncOptions(data, url, branch)
11251144
syncManifests, err := sync.Generate(syncOpts)
11261145
if err != nil {
11271146
return nil, fmt.Errorf("could not generate sync manifests: %w", err)
11281147
}
1148+
11291149
repositoryFiles[syncManifests.Path] = syncManifests.Content
11301150
repositoryFiles[filepath.Join(data.Path.ValueString(), data.Namespace.ValueString(), konfig.DefaultKustomizationFileName())] = getKustomizationFile(data)
1151+
11311152
return repositoryFiles, nil
11321153
}
11331154

internal/provider/resource_bootstrap_git_test.go

+54
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"os"
2525
"path/filepath"
2626
"regexp"
27+
"runtime"
2728
"strings"
2829
"testing"
2930
"time"
@@ -45,6 +46,7 @@ import (
4546
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
4647
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
4748
"github.com/hashicorp/terraform-plugin-testing/terraform"
49+
cp "github.com/otiai10/copy"
4850
"github.com/stretchr/testify/require"
4951
corev1 "k8s.io/api/core/v1"
5052
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -182,6 +184,34 @@ func TestAccBootstrapGit_SSH(t *testing.T) {
182184
})
183185
}
184186

187+
func TestAccBootstrapGit_AirGapped(t *testing.T) {
188+
env := setupEnvironment(t)
189+
resource.ParallelTest(t, resource.TestCase{
190+
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
191+
Steps: []resource.TestStep{
192+
{
193+
PreConfig: func() {
194+
tmpBase := t.TempDir()
195+
tmpBase, err := filepath.EvalSymlinks(tmpBase)
196+
require.NoError(t, err)
197+
198+
_, b, _, _ := runtime.Caller(0)
199+
manifestsBase := filepath.Join(filepath.Dir(b), "../..", "manifests")
200+
err = cp.Copy(manifestsBase, tmpBase)
201+
require.NoError(t, err)
202+
EmbeddedManifests = tmpBase
203+
},
204+
Config: bootstrapAirGapped(env),
205+
Check: resource.ComposeTestCheckFunc(
206+
resource.TestCheckResourceAttrSet("flux_bootstrap_git.this", "repository_files.flux-system/kustomization.yaml"),
207+
resource.TestCheckResourceAttrSet("flux_bootstrap_git.this", "repository_files.flux-system/gotk-components.yaml"),
208+
resource.TestCheckResourceAttrSet("flux_bootstrap_git.this", "repository_files.flux-system/gotk-sync.yaml"),
209+
),
210+
},
211+
},
212+
})
213+
}
214+
185215
func TestAccBootstrapGit_Drift(t *testing.T) {
186216
env := setupEnvironment(t)
187217
resource.ParallelTest(t, resource.TestCase{
@@ -526,6 +556,30 @@ EOF
526556
`, env.kubeCfgPath, env.sshClone, env.privateKey)
527557
}
528558

559+
func bootstrapAirGapped(env environment) string {
560+
return fmt.Sprintf(`
561+
provider "flux" {
562+
kubernetes = {
563+
config_path = "%s"
564+
}
565+
git = {
566+
url = "%s"
567+
ssh = {
568+
username = "git"
569+
private_key = <<EOF
570+
%s
571+
EOF
572+
}
573+
}
574+
}
575+
576+
resource "flux_bootstrap_git" "this" {
577+
embedded_manifests = true
578+
version = "v0.0.0"
579+
}
580+
`, env.kubeCfgPath, env.sshClone, env.privateKey)
581+
}
582+
529583
func bootstrapGitVersion(env environment, version string) string {
530584
return fmt.Sprintf(`
531585
provider "flux" {

0 commit comments

Comments
 (0)