Skip to content

Commit 7eba9ca

Browse files
committed
'skaffold init' for Kustomize projects generates profiles for each overlay
1 parent 286fa95 commit 7eba9ca

File tree

30 files changed

+448
-82
lines changed

30 files changed

+448
-82
lines changed

cmd/skaffold/app/cmd/init.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ const maxFileSize = 1024 * 1024 * 512
3131

3232
var (
3333
composeFile string
34+
buildpacksBuilder string
35+
defaultKustomization string
3436
cliArtifacts []string
3537
cliKubernetesManifests []string
3638
skipBuild bool
@@ -42,7 +44,6 @@ var (
4244
enableBuildpacksInit bool
4345
enableNewInitFormat bool
4446
enableManifestGeneration bool
45-
buildpacksBuilder string
4647
)
4748

4849
// for testing
@@ -59,8 +60,9 @@ func NewCmdInit() *cobra.Command {
5960
f.MarkHidden("skip-deploy")
6061
f.BoolVar(&force, "force", false, "Force the generation of the Skaffold config")
6162
f.StringVar(&composeFile, "compose-file", "", "Initialize from a docker-compose file")
63+
f.StringVar(&defaultKustomization, "default-kustomization", "", "Default Kustomization overlay path (others will be added as profiles)")
6264
f.StringArrayVarP(&cliArtifacts, "artifact", "a", nil, "'='-delimited Dockerfile/image pair, or JSON string, to generate build artifact\n(example: --artifact='{\"builder\":\"Docker\",\"payload\":{\"path\":\"/web/Dockerfile.web\"},\"image\":\"gcr.io/web-project/image\"}')")
63-
f.StringArrayVarP(&cliKubernetesManifests, "kubernetes-manifest", "k", nil, "a path or a glob pattern to kubernetes manifests (can be non-existent) to be added to the kubectl deployer (overrides detection of kubernetes manifests). Repeat the flag for multiple entries. E.g.: skaffold init -k pod.yaml -k k8s/*.yml")
65+
f.StringArrayVarP(&cliKubernetesManifests, "kubernetes-manifest", "k", nil, "A path or a glob pattern to kubernetes manifests (can be non-existent) to be added to the kubectl deployer (overrides detection of kubernetes manifests). Repeat the flag for multiple entries. E.g.: skaffold init -k pod.yaml -k k8s/*.yml")
6466
f.BoolVar(&analyze, "analyze", false, "Print all discoverable Dockerfiles and images in JSON format to stdout")
6567
f.BoolVar(&enableNewInitFormat, "XXenableNewInitFormat", false, "")
6668
f.MarkHidden("XXenableNewInitFormat")
@@ -80,7 +82,9 @@ func NewCmdInit() *cobra.Command {
8082

8183
func doInit(ctx context.Context, out io.Writer) error {
8284
return initEntrypoint(ctx, out, config.Config{
85+
BuildpacksBuilder: buildpacksBuilder,
8386
ComposeFile: composeFile,
87+
DefaultKustomization: defaultKustomization,
8488
CliArtifacts: cliArtifacts,
8589
CliKubernetesManifests: cliKubernetesManifests,
8690
SkipBuild: skipBuild,
@@ -92,7 +96,6 @@ func doInit(ctx context.Context, out io.Writer) error {
9296
EnableBuildpacksInit: enableBuildpacksInit,
9397
EnableNewInitFormat: enableNewInitFormat || enableBuildpacksInit || enableJibInit,
9498
EnableManifestGeneration: enableManifestGeneration,
95-
BuildpacksBuilder: buildpacksBuilder,
9699
Opts: opts,
97100
MaxFileSize: maxFileSize,
98101
})

docs/content/en/docs/pipeline-stages/init.md

+59-15
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@ Skaffold auto-generates `build` and `deploy` config for supported builders and d
1111

1212

1313
## Build Config Initialization
14-
`skaffold init` currently supports build detection for two builders.
14+
`skaffold init` currently supports build detection for two builders:
1515

1616
1. [Docker]({{<relref "/docs/pipeline-stages/builders/docker">}})
1717
2. [Jib]({{<relref "/docs/pipeline-stages/builders/jib">}})
1818

1919
`skaffold init` will walk your project directory and look for any `Dockerfiles`
2020
or `build.gradle/pom.xml`. Please note, `skaffold init` skips files that are larger than 500MB.
2121

22-
If you have multiple `Dockerfile` or `build.gradle/pom.xml` files, Skaffold will provide an option
23-
to pair an image with one of the file.
22+
If you have multiple `Dockerfile` or `build.gradle/pom.xml` files, Skaffold will prompt you to
23+
pair your build config files with any images detected in your deploy configuration.
2424

25-
E.g. For a multi-services [microservices example](https://github.com/GoogleContainerTools/skaffold/tree/master/examples/microservices)
25+
E.g. For an application with [two microservices] (https://github.com/GoogleContainerTools/skaffold/tree/master/examples/microservices):
2626

2727
```bash
2828
skaffold init
@@ -31,15 +31,15 @@ skaffold init
3131

3232

3333
{{< alert title="Note" >}}
34-
You can choose <code>None (image not built from these sources)</code> in case none of the suggested
35-
options are correct. <br>
36-
You will have to manually set up build config for this artifact
34+
You can choose <code>None (image not built from these sources)</code> if none of the suggested
35+
options are correct, or this image is not built by any of your source code.<br>
36+
If this image is one you want Skaffold to build, you'll need to manually set up the build configuration for this artifact.
3737
{{</alert>}}
3838

39-
`skaffold` init also recognizes a maven or gradle project and will auto-suggest [`jib`]({{<relref "/docs/pipeline-stages/builders#/local#jib-maven-and-gradle">}}) builder.
40-
Currently `jib` artifact detection is disabled by default, you can turn it on using the flag `--XXenableJibInit`.
39+
`skaffold` init also recognizes Maven and Gradle projects, and will auto-suggest the [`jib`]({{<relref "/docs/pipeline-stages/builders#/local#jib-maven-and-gradle">}}) builder.
40+
Currently `jib` artifact detection is disabled by default, but can be enabled using the flag `--XXenableJibInit`.
4141

42-
You can try it this out on example [jib project](https://github.com/GoogleContainerTools/skaffold/tree/master/examples/jib-multimodule)
42+
You can try this out on our example [jib project](https://github.com/GoogleContainerTools/skaffold/tree/master/examples/jib-multimodule)
4343

4444
```bash
4545
skaffold init --XXenableJibInit
@@ -48,11 +48,10 @@ skaffold init --XXenableJibInit
4848
![jib-multimodule](/images/jib-multimodule-init-flow.png)
4949

5050

51-
In case you want to configure build artifacts on your own, use `--skip-build` flag.
52-
5351
## Deploy Config Initialization
54-
`skaffold init` currently supports only [`kubectl` deployer]({{<relref "/docs/pipeline-stages/deployers#deploying-with-kubectl" >}})
55-
Skaffold will walk through all the `yaml` files in your project and find valid kubernetes manifest files.
52+
`skaffold init` support bootstrapping projects set up to deploy with [`kubectl`]({{<relref "/docs/pipeline-stages/deployers#deploying-with-kubectl" >}})
53+
or [`kustomize`]({{<relref "/docs/pipeline-stages/deployers#deploying-with-kubectl" >}}).
54+
For projects deploying straight through `kubectl`, Skaffold will walk through all the `yaml` files in your project and find valid Kubernetes manifest files.
5655

5756
These files will be added to `deploy` config in `skaffold.yaml`.
5857

@@ -64,15 +63,60 @@ deploy:
6463
- leeroy-web/kubernetes/deployment.yaml
6564
```
6665
66+
For projects deploying with `kustomize`, Skaffold will scan your project and look for `kustomization.yaml`s as well as Kubernetes manifests.
67+
It will attempt to infer the project structure based on the recommended project structure from the Kustomize project: thus,
68+
**it is highly recommended to match your project structure to the recommended base/ and overlay/ structure from Kustomize!**
69+
70+
This generally looks like this:
71+
72+
```
73+
app/ <- application source code, along with build configuration
74+
main.go
75+
Dockerfile
76+
...
77+
base/ <- base deploy configuration
78+
kustomization.yaml
79+
deployment.yaml
80+
overlays/ <- one or more nested directories, each with modified environment configuration
81+
dev/
82+
deployment.yaml
83+
kustomization.yaml
84+
prod/
85+
...
86+
```
87+
88+
When overlay directories are found, these will be listed in the generated Skaffold config as `paths` in the `kustomize` deploy stanza. However, it generally does not make sense to have multiple overlays applied at the same time, so **Skaffold will attempt to choose a default overlay, and put each other overlay into its own profile**. This can be specified by the user through the flag `--default-kustomization`; otherwise, Skaffold will use the following heuristic:
89+
90+
1) Any overlay with the name `dev`
91+
2) If none present, the **first** overlay that isn't named `prod`
92+
93+
*Note: order is guaranteed, since Skaffold's directory parsing is always deterministic.*
6794

6895
## Init API
69-
`skaffold init` also exposes an api which tools like IDEs can integrate with via flags.
96+
`skaffold init` also exposes an AP:I which tools like IDEs can integrate with via flags.
7097

7198
This API can be used to
7299

73100
1. Analyze a project workspace and discover all build definitions (e.g. `Dockerfile`s) and artifacts (image names from the Kubernetes manifests) - this then provides an ability for tools to ask the user to pair the artifacts with Dockerfiles interactively.
74101
2. Given a pairing between the image names (artifacts) and build definitions (e.g. Dockerfiles), generate Skaffold `build` config for a given artifact.
75102

103+
The resulting `skaffold.yaml` will look something like this:
104+
105+
```yaml
106+
apiVersion: skaffold/v2beta5
107+
...
108+
deploy:
109+
kustomize:
110+
paths:
111+
- overlays/dev
112+
profiles:
113+
- name: prod
114+
deploy:
115+
kustomize:
116+
paths:
117+
- overlays/prod
118+
```
119+
76120
**Init API contract**
77121

78122
| API | flag | input/output |

docs/content/en/docs/references/cli/_index.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -663,9 +663,10 @@ Options:
663663
-a, --artifact=[]: '='-delimited Dockerfile/image pair, or JSON string, to generate build artifact
664664
(example: --artifact='{"builder":"Docker","payload":{"path":"/web/Dockerfile.web"},"image":"gcr.io/web-project/image"}')
665665
--compose-file='': Initialize from a docker-compose file
666+
--default-kustomization='': Default Kustomization overlay path (others will be added as profiles)
666667
-f, --filename='skaffold.yaml': Path or URL to the Skaffold config file
667668
--force=false: Force the generation of the Skaffold config
668-
-k, --kubernetes-manifest=[]: a path or a glob pattern to kubernetes manifests (can be non-existent) to be added to the kubectl deployer (overrides detection of kubernetes manifests). Repeat the flag for multiple entries. E.g.: skaffold init -k pod.yaml -k k8s/*.yml
669+
-k, --kubernetes-manifest=[]: A path or a glob pattern to kubernetes manifests (can be non-existent) to be added to the kubectl deployer (overrides detection of kubernetes manifests). Repeat the flag for multiple entries. E.g.: skaffold init -k pod.yaml -k k8s/*.yml
669670
--skip-build=false: Skip generating build artifacts in Skaffold config
670671
671672
Usage:
@@ -680,6 +681,7 @@ Env vars:
680681
* `SKAFFOLD_ANALYZE` (same as `--analyze`)
681682
* `SKAFFOLD_ARTIFACT` (same as `--artifact`)
682683
* `SKAFFOLD_COMPOSE_FILE` (same as `--compose-file`)
684+
* `SKAFFOLD_DEFAULT_KUSTOMIZATION` (same as `--default-kustomization`)
683685
* `SKAFFOLD_FILENAME` (same as `--filename`)
684686
* `SKAFFOLD_FORCE` (same as `--force`)
685687
* `SKAFFOLD_KUBERNETES_MANIFEST` (same as `--kubernetes-manifest`)

examples/getting-started-kustomize/deployment.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,3 @@ spec:
1313
metadata:
1414
labels:
1515
app: skaffold-kustomize
16-

integration/examples/getting-started-kustomize/deployment.yaml renamed to integration/examples/getting-started-kustomize/base/deployment.yaml

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ metadata:
55
labels:
66
app: skaffold-kustomize
77
spec:
8-
replicas: 1
98
selector:
109
matchLabels:
1110
app: skaffold-kustomize
1211
template:
1312
metadata:
1413
labels:
1514
app: skaffold-kustomize
16-
15+
spec:
16+
containers:
17+
- name: skaffold-kustomize
18+
image: skaffold-kustomize
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
apiVersion: kustomize.config.k8s.io/v1beta1
2+
kind: Kustomization
3+
4+
resources:
5+
- deployment.yaml

integration/examples/getting-started-kustomize/kustomization.yaml

-4
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: skaffold-kustomize
5+
labels:
6+
env: dev
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: kustomize.config.k8s.io/v1beta1
2+
kind: Kustomization
3+
4+
# namespace: dev
5+
nameSuffix: -dev
6+
7+
patchesStrategicMerge:
8+
- deployment.yaml
9+
10+
resources:
11+
- ../../base
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: skaffold-kustomize
5+
labels:
6+
env: prod
7+
spec:
8+
replicas: 3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: kustomize.config.k8s.io/v1beta1
2+
kind: Kustomization
3+
4+
# namespace: prod
5+
nameSuffix: -prod
6+
7+
patchesStrategicMerge:
8+
- deployment.yaml
9+
10+
resources:
11+
- ../../base
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: skaffold-kustomize
5+
labels:
6+
env: staging
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: kustomize.config.k8s.io/v1beta1
2+
kind: Kustomization
3+
4+
# namespace: staging
5+
nameSuffix: -staging
6+
7+
patchesStrategicMerge:
8+
- deployment.yaml
9+
10+
resources:
11+
- ../../base

integration/examples/getting-started-kustomize/patch.yaml

-10
This file was deleted.

integration/examples/getting-started-kustomize/skaffold.yaml

+16-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@ metadata:
44
name: getting-started-kustomize
55
build:
66
artifacts:
7-
- image: hello-world
7+
- image: skaffold-kustomize
8+
context: app
89
deploy:
9-
kustomize: {}
10+
kustomize:
11+
paths:
12+
- overlays/dev
13+
profiles:
14+
- name: prod
15+
deploy:
16+
kustomize:
17+
paths:
18+
- overlays/prod
19+
- name: staging
20+
deploy:
21+
kustomize:
22+
paths:
23+
- overlays/staging

integration/run_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ func TestRun(t *testing.T) {
116116
{
117117
description: "kustomize",
118118
dir: "examples/getting-started-kustomize",
119-
deployments: []string{"skaffold-kustomize"},
119+
deployments: []string{"skaffold-kustomize-dev"},
120120
},
121121
}
122122
for _, test := range tests {

pkg/skaffold/deploy/kustomize.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import (
4545
var (
4646
DefaultKustomizePath = "."
4747
kustomizeFilePaths = []string{"kustomization.yaml", "kustomization.yml", "Kustomization"}
48+
basePath = "base"
4849
)
4950

5051
type patchPath struct {
@@ -261,7 +262,7 @@ func dependenciesForKustomization(dir string) ([]string, error) {
261262
candidates := append(content.Bases, content.Resources...)
262263

263264
for _, candidate := range candidates {
264-
// If the file doesn't exist locally, we can assume it's a remote file and
265+
// If the file doesn't exist locally, we can assume it's a remote file and
265266
// skip it, since we can't monitor remote files. Kustomize itself will
266267
// handle invalid/missing files.
267268
local, mode := pathExistsLocally(candidate, dir)
@@ -313,8 +314,7 @@ func dependenciesForKustomization(dir string) ([]string, error) {
313314
// A Kustomization config must be at the root of the directory. Kustomize will
314315
// error if more than one of these files exists so order doesn't matter.
315316
func findKustomizationConfig(dir string) (string, error) {
316-
candidates := []string{"kustomization.yaml", "kustomization.yml", "Kustomization"}
317-
for _, candidate := range candidates {
317+
for _, candidate := range kustomizeFilePaths {
318318
if local, _ := pathExistsLocally(candidate, dir); local {
319319
return filepath.Join(dir, candidate), nil
320320
}
@@ -368,6 +368,10 @@ func buildCommandArgs(buildArgs []string, kustomizePath string) []string {
368368
return args
369369
}
370370

371+
func IsKustomizationBase(path string) bool {
372+
return filepath.Dir(path) == basePath
373+
}
374+
371375
func IsKustomizationPath(path string) bool {
372376
filename := filepath.Base(path)
373377
for _, candidate := range kustomizeFilePaths {

pkg/skaffold/initializer/analyze/analyze.go

+4
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ func (a *ProjectAnalysis) KustomizePaths() []string {
5959
return a.kustomizeAnalyzer.kustomizePaths
6060
}
6161

62+
func (a *ProjectAnalysis) KustomizeBases() []string {
63+
return a.kustomizeAnalyzer.bases
64+
}
65+
6266
func (a *ProjectAnalysis) analyzers() []analyzer {
6367
return []analyzer{
6468
a.kubeAnalyzer,

pkg/skaffold/initializer/analyze/kustomize.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,18 @@ import (
2626
// kustomizeAnalyzer is a Visitor during the directory analysis that finds kustomize files
2727
type kustomizeAnalyzer struct {
2828
directoryAnalyzer
29+
30+
bases []string
2931
kustomizePaths []string
3032
}
3133

32-
func (k *kustomizeAnalyzer) analyzeFile(filePath string) error {
33-
if !schema.IsSkaffoldConfig(filePath) && deploy.IsKustomizationPath(filePath) {
34-
k.kustomizePaths = append(k.kustomizePaths, filepath.Dir(filePath))
34+
func (k *kustomizeAnalyzer) analyzeFile(path string) error {
35+
switch {
36+
case schema.IsSkaffoldConfig(path):
37+
case deploy.IsKustomizationBase(path):
38+
k.bases = append(k.bases, filepath.Dir(path))
39+
case deploy.IsKustomizationPath(path):
40+
k.kustomizePaths = append(k.kustomizePaths, filepath.Dir(path))
3541
}
3642
return nil
3743
}

0 commit comments

Comments
 (0)