-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
342 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
/* | ||
Copyright 2021 The Skaffold Authors | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
package generate | ||
|
||
import ( | ||
"context" | ||
"io/ioutil" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/sirupsen/logrus" | ||
|
||
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kustomize" | ||
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" | ||
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" | ||
latestV2 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v2" | ||
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" | ||
) | ||
|
||
// NewGenerator instantiates a Generator object. | ||
func NewGenerator(workingDir string, config latestV2.Generate) *Generator { | ||
return &Generator{ | ||
workingDir: workingDir, | ||
config: config, | ||
} | ||
} | ||
|
||
// Generator provides the functions for the manifest sources (raw manifests, helm charts, kustomize configs and remote packages). | ||
type Generator struct { | ||
workingDir string | ||
config latestV2.Generate | ||
} | ||
|
||
// Generate parses the config resources from the paths in .Generate.Manifests. This path can be the path to raw manifest, | ||
// kustomize manifests, helm charts or kpt function configs. All should be file-watched. | ||
func (g *Generator) Generate(ctx context.Context) (manifest.ManifestList, error) { | ||
// exclude remote url. | ||
var paths []string | ||
for _, path := range g.config.Manifests { | ||
switch { | ||
case util.IsURL(path): | ||
// TODO(yuwenma): remote URL should be changed to use kpt package management approach, via API Schema | ||
// `render.generate.remotePackages` | ||
case strings.HasPrefix(path, "gs://"): | ||
// TODO(yuwenma): handle GS packages. | ||
default: | ||
paths = append(paths, path) | ||
} | ||
} | ||
|
||
// expend the glob paths. | ||
expanded, err := util.ExpandPathsGlob(g.workingDir, paths) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Parse kustomize manifests and non-kustomize manifests. We may also want to parse (and exclude) kpt function manifests later. | ||
// TODO: Update `kustomize build` to kustomize kpt-fn once https://github.com/GoogleContainerTools/kpt/issues/1447 is fixed. | ||
kustomizePathMap := make(map[string]bool) | ||
var nonKustomizePaths []string | ||
for _, path := range expanded { | ||
if dir, ok := isKustomizeDir(path); ok { | ||
kustomizePathMap[dir] = true | ||
} | ||
} | ||
for _, path := range expanded { | ||
kustomizeDirDup := false | ||
for kPath := range kustomizePathMap { | ||
// Before kustomize kpt-fn can provide a way to parse the kustomize content, we assume the users do not place non-kustomize manifests under the kustomization.yaml directory. | ||
if strings.HasPrefix(path, kPath) { | ||
kustomizeDirDup = true | ||
break | ||
} | ||
} | ||
if !kustomizeDirDup { | ||
nonKustomizePaths = append(nonKustomizePaths, path) | ||
} | ||
} | ||
|
||
var manifests manifest.ManifestList | ||
for kPath := range kustomizePathMap { | ||
// TODO: support kustomize buildArgs (shall we support it in kpt-fn)? | ||
cmd := exec.CommandContext(ctx, "kustomize", "build", kPath) | ||
out, err := util.RunCmdOut(cmd) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if len(out) == 0 { | ||
continue | ||
} | ||
manifests.Append(out) | ||
} | ||
for _, nkPath := range nonKustomizePaths { | ||
if !kubernetes.HasKubernetesFileExtension(nkPath) { | ||
if !util.StrSliceContains(g.config.Manifests, nkPath) { | ||
logrus.Infof("refusing to deploy/delete non {json, yaml} file %s", nkPath) | ||
logrus.Info("If you still wish to deploy this file, please specify it directly, outside a glob pattern.") | ||
continue | ||
} | ||
} | ||
manifestFileContent, err := ioutil.ReadFile(nkPath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
manifests.Append(manifestFileContent) | ||
} | ||
// TODO(yuwenma): helm resources. `render.generate.helmCharts` | ||
return manifests, nil | ||
} | ||
|
||
// isKustomizeDir checks if the path is managed by kustomize. A more reliable approach is parsing the kustomize content | ||
// resources, bases, overlays. However, this switches the manifests parsing from kustomize/kpt to skaffold. To avoid | ||
// skaffold render.generate mis-use, we expect the users do not place non-kustomize manifests under the kustomization.yaml directory, so as the kpt manifests. | ||
func isKustomizeDir(path string) (string, bool) { | ||
fileInfo, err := os.Stat(path) | ||
if err != nil { | ||
return "", false | ||
} | ||
var dir string | ||
switch mode := fileInfo.Mode(); { | ||
// TODO: Check if regular file contains kpt functions. if so, we may want to abstract that info as well. | ||
case mode.IsDir(): | ||
dir = path | ||
case mode.IsRegular(): | ||
dir = filepath.Dir(path) | ||
} | ||
|
||
for _, base := range kustomize.KustomizeFilePaths { | ||
if _, err := os.Stat(filepath.Join(dir, base)); os.IsNotExist(err) { | ||
continue | ||
} | ||
return dir, true | ||
} | ||
return "", false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
/* | ||
Copyright 2021 The Skaffold Authors | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
package generate | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" | ||
latestV2 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v2" | ||
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" | ||
"github.com/GoogleContainerTools/skaffold/testutil" | ||
) | ||
|
||
const ( | ||
// Test file under <tmp>/pod.yaml | ||
podYaml = `apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: leeroy-web | ||
spec: | ||
containers: | ||
- name: leeroy-web | ||
image: leeroy-web | ||
` | ||
|
||
// Test file under <tmp>/pods.yaml. This file contains multiple config object. | ||
podsYaml = `apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: leeroy-web2 | ||
spec: | ||
containers: | ||
- name: leeroy-web2 | ||
image: leeroy-web2 | ||
--- | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: leeroy-web3 | ||
spec: | ||
containers: | ||
- name: leeroy-web3 | ||
image: leeroy-web3 | ||
` | ||
|
||
// Test file under <tmp>/base/patch.yaml | ||
patchYaml = `apiVersion: apps/v1 | ||
kind: Deployment | ||
metadata: | ||
name: kustomize-test | ||
spec: | ||
template: | ||
spec: | ||
containers: | ||
- name: kustomize-test | ||
image: index.docker.io/library/busybox | ||
command: | ||
- sleep | ||
- "3600" | ||
` | ||
// Test file under <tmp>/base/deployment.yaml | ||
kustomizeDeploymentYaml = `apiVersion: apps/v1 | ||
kind: Deployment | ||
metadata: | ||
name: kustomize-test | ||
labels: | ||
app: kustomize-test | ||
spec: | ||
replicas: 1 | ||
selector: | ||
matchLabels: | ||
app: kustomize-test | ||
template: | ||
metadata: | ||
labels: | ||
app: kustomize-test | ||
spec: | ||
containers: | ||
- name: kustomize-test | ||
image: not/a/valid/image | ||
` | ||
// The kustomize build result from kustomizeYaml file. | ||
kustomizePatchedOutput = `apiVersion: apps/v1 | ||
kind: Deployment | ||
metadata: | ||
labels: | ||
app: kustomize-test | ||
name: kustomize-test | ||
spec: | ||
replicas: 1 | ||
selector: | ||
matchLabels: | ||
app: kustomize-test | ||
template: | ||
metadata: | ||
labels: | ||
app: kustomize-test | ||
spec: | ||
containers: | ||
- command: | ||
- sleep | ||
- "3600" | ||
image: index.docker.io/library/busybox | ||
name: kustomize-test | ||
` | ||
// Test file under <tmp>/base/kustomization.yaml | ||
kustomizeYaml = `resources: | ||
- deployment.yaml | ||
patches: | ||
- patch.yaml | ||
` | ||
) | ||
|
||
func TestGenerate(t *testing.T) { | ||
tests := []struct { | ||
description string | ||
generateConfig latestV2.Generate | ||
expected manifest.ManifestList | ||
}{ | ||
{ | ||
description: "render raw manifests", | ||
generateConfig: latestV2.Generate{ | ||
Manifests: []string{"pod.yaml"}, | ||
}, | ||
expected: manifest.ManifestList{[]byte(podYaml)}, | ||
}, | ||
{ | ||
description: "render glob raw manifests", | ||
generateConfig: latestV2.Generate{ | ||
Manifests: []string{"*.yaml"}, | ||
}, | ||
expected: manifest.ManifestList{[]byte(podYaml), []byte(podsYaml)}, | ||
}, | ||
{ | ||
description: "render kustomize manifests", | ||
generateConfig: latestV2.Generate{ | ||
Manifests: []string{"base"}, | ||
}, | ||
expected: manifest.ManifestList{[]byte(kustomizePatchedOutput)}, | ||
}, | ||
|
||
{ | ||
description: "render mixed raw and kustomize manifests", | ||
generateConfig: latestV2.Generate{ | ||
Manifests: []string{"*"}, | ||
}, | ||
expected: manifest.ManifestList{[]byte(kustomizePatchedOutput), []byte(podYaml), []byte(podsYaml)}, | ||
}, | ||
} | ||
for _, test := range tests { | ||
testutil.Run(t, test.description, func(t *testutil.T) { | ||
|
||
fakeCmd := testutil.CmdRunOut("kustomize build base", kustomizePatchedOutput) | ||
t.Override(&util.DefaultExecCommand, fakeCmd) | ||
|
||
t.NewTempDir(). | ||
Write("pod.yaml", podYaml). | ||
Write("pods.yaml", podsYaml). | ||
Write("base/kustomization.yaml", kustomizeYaml). | ||
Write("base/patch.yaml", patchYaml). | ||
Write("base/deployment.yaml", kustomizeDeploymentYaml). | ||
Touch("empty.ignored"). | ||
Chdir() | ||
|
||
g := NewGenerator(".", test.generateConfig) | ||
actual, err := g.Generate(context.Background()) | ||
t.CheckNoError(err) | ||
t.CheckDeepEqual(actual.String(), test.expected.String()) | ||
}) | ||
} | ||
} |