github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/render/generate/generate.go (about) 1 /* 2 Copyright 2021 The Skaffold Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package generate 18 19 import ( 20 "context" 21 "io/ioutil" 22 "os" 23 "os/exec" 24 "path/filepath" 25 "strings" 26 27 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/kustomize" 28 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes" 29 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest" 30 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log" 31 latestV2 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v2" 32 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" 33 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util/stringslice" 34 ) 35 36 // NewGenerator instantiates a Generator object. 37 func NewGenerator(workingDir string, config latestV2.Generate) *Generator { 38 return &Generator{ 39 workingDir: workingDir, 40 config: config, 41 } 42 } 43 44 // Generator provides the functions for the manifest sources (raw manifests, helm charts, kustomize configs and remote packages). 45 type Generator struct { 46 workingDir string 47 config latestV2.Generate 48 } 49 50 // Generate parses the config resources from the paths in .Generate.Manifests. This path can be the path to raw manifest, 51 // kustomize manifests, helm charts or kpt function configs. All should be file-watched. 52 func (g *Generator) Generate(ctx context.Context) (manifest.ManifestList, error) { 53 // exclude remote url. 54 var paths []string 55 // TODO(yuwenma): Apply new UX, kustomize kpt and helm 56 for _, path := range g.config.RawK8s { 57 switch { 58 case util.IsURL(path): 59 // TODO(yuwenma): remote URL should be changed to use kpt package management approach, via API Schema 60 // `render.generate.remotePackages` 61 case strings.HasPrefix(path, "gs://"): 62 // TODO(yuwenma): handle GS packages. 63 default: 64 paths = append(paths, path) 65 } 66 } 67 // expend the glob paths. 68 expanded, err := util.ExpandPathsGlob(g.workingDir, paths) 69 if err != nil { 70 return nil, err 71 } 72 73 // Parse kustomize manifests and non-kustomize manifests. We may also want to parse (and exclude) kpt function manifests later. 74 // TODO: Update `kustomize build` to kustomize kpt-fn once https://github.com/GoogleContainerTools/kpt/issues/1447 is fixed. 75 kustomizePathMap := make(map[string]bool) 76 var nonKustomizePaths []string 77 for _, path := range expanded { 78 if dir, ok := isKustomizeDir(path); ok { 79 kustomizePathMap[dir] = true 80 } 81 } 82 for _, path := range expanded { 83 kustomizeDirDup := false 84 for kPath := range kustomizePathMap { 85 // 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. 86 if strings.HasPrefix(path, kPath) { 87 kustomizeDirDup = true 88 break 89 } 90 } 91 if !kustomizeDirDup { 92 nonKustomizePaths = append(nonKustomizePaths, path) 93 } 94 } 95 96 var manifests manifest.ManifestList 97 for kPath := range kustomizePathMap { 98 // TODO: support kustomize buildArgs (shall we support it in kpt-fn)? 99 cmd := exec.CommandContext(ctx, "kustomize", "build", kPath) 100 out, err := util.RunCmdOut(ctx, cmd) 101 if err != nil { 102 return nil, err 103 } 104 if len(out) == 0 { 105 continue 106 } 107 manifests.Append(out) 108 } 109 for _, nkPath := range nonKustomizePaths { 110 if !kubernetes.HasKubernetesFileExtension(nkPath) { 111 if !stringslice.Contains(g.config.RawK8s, nkPath) { 112 log.Entry(ctx).Infof("refusing to deploy/delete non {json, yaml} file %s", nkPath) 113 log.Entry(ctx).Info("If you still wish to deploy this file, please specify it directly, outside a glob pattern.") 114 continue 115 } 116 } 117 manifestFileContent, err := ioutil.ReadFile(nkPath) 118 if err != nil { 119 return nil, err 120 } 121 manifests.Append(manifestFileContent) 122 } 123 // TODO(yuwenma): helm resources. `render.generate.helmCharts` 124 return manifests, nil 125 } 126 127 // isKustomizeDir checks if the path is managed by kustomize. A more reliable approach is parsing the kustomize content 128 // resources, bases, overlays. However, this switches the manifests parsing from kustomize/kpt to skaffold. To avoid 129 // 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. 130 func isKustomizeDir(path string) (string, bool) { 131 fileInfo, err := os.Stat(path) 132 if err != nil { 133 return "", false 134 } 135 var dir string 136 switch mode := fileInfo.Mode(); { 137 // TODO: Check if regular file contains kpt functions. if so, we may want to abstract that info as well. 138 case mode.IsDir(): 139 dir = path 140 case mode.IsRegular(): 141 dir = filepath.Dir(path) 142 } 143 144 for _, base := range kustomize.KustomizeFilePaths { 145 if _, err := os.Stat(filepath.Join(dir, base)); os.IsNotExist(err) { 146 continue 147 } 148 return dir, true 149 } 150 return "", false 151 }