Skip to content

Commit 99870a8

Browse files
authored
Merge pull request #3533 from balopat/init_refactor/analyzers
[init refactor] introducing init analyzers
2 parents 871be06 + b74d2f0 commit 99870a8

File tree

7 files changed

+429
-177
lines changed

7 files changed

+429
-177
lines changed

pkg/skaffold/initializer/analyze.go

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
Copyright 2020 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 initializer
18+
19+
import (
20+
"fmt"
21+
"path/filepath"
22+
"sort"
23+
24+
"github.com/karrick/godirwalk"
25+
"github.com/sirupsen/logrus"
26+
27+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/initializer/kubectl"
28+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
29+
)
30+
31+
type analysis struct {
32+
kubectlAnalyzer *kubectlAnalyzer
33+
skaffoldAnalyzer *skaffoldConfigAnalyzer
34+
builderAnalyzer *builderAnalyzer
35+
}
36+
37+
// analyzer is a generic Visitor that is called on every file in the directory
38+
// It can manage state and react to walking events assuming a bread first search
39+
type analyzer interface {
40+
enterDir(dir string)
41+
analyzeFile(file string) error
42+
exitDir(dir string)
43+
}
44+
45+
type directoryAnalyzer struct {
46+
currentDir string
47+
}
48+
49+
func (a *directoryAnalyzer) analyzeFile(filePath string) error {
50+
return nil
51+
}
52+
53+
func (a *directoryAnalyzer) enterDir(dir string) {
54+
a.currentDir = dir
55+
}
56+
57+
func (a *directoryAnalyzer) exitDir(dir string) {
58+
//pass
59+
}
60+
61+
type kubectlAnalyzer struct {
62+
directoryAnalyzer
63+
kubernetesManifests []string
64+
}
65+
66+
func (a *kubectlAnalyzer) analyzeFile(filePath string) error {
67+
if kubectl.IsKubernetesManifest(filePath) && !IsSkaffoldConfig(filePath) {
68+
a.kubernetesManifests = append(a.kubernetesManifests, filePath)
69+
}
70+
return nil
71+
}
72+
73+
type skaffoldConfigAnalyzer struct {
74+
directoryAnalyzer
75+
force bool
76+
}
77+
78+
func (a *skaffoldConfigAnalyzer) analyzeFile(filePath string) error {
79+
if !IsSkaffoldConfig(filePath) {
80+
return nil
81+
}
82+
if !a.force {
83+
return fmt.Errorf("pre-existing %s found (you may continue with --force)", filePath)
84+
}
85+
logrus.Debugf("%s is a valid skaffold configuration: continuing since --force=true", filePath)
86+
return nil
87+
}
88+
89+
type builderAnalyzer struct {
90+
directoryAnalyzer
91+
enableJibInit bool
92+
enableBuildpackInit bool
93+
findBuilders bool
94+
foundBuilders []InitBuilder
95+
96+
parentDirToStopFindBuilders string
97+
}
98+
99+
func (a *builderAnalyzer) analyzeFile(filePath string) error {
100+
if a.findBuilders && (a.parentDirToStopFindBuilders == "" || a.parentDirToStopFindBuilders == a.currentDir) {
101+
builderConfigs, continueSearchingBuilders := detectBuilders(a.enableJibInit, a.enableBuildpackInit, filePath)
102+
a.foundBuilders = append(a.foundBuilders, builderConfigs...)
103+
if !continueSearchingBuilders {
104+
a.parentDirToStopFindBuilders = a.currentDir
105+
}
106+
}
107+
return nil
108+
}
109+
110+
func (a *builderAnalyzer) exitDir(dir string) {
111+
if a.parentDirToStopFindBuilders == dir {
112+
a.parentDirToStopFindBuilders = ""
113+
}
114+
}
115+
116+
// analyze recursively walks a directory and returns the k8s configs and builder configs that it finds
117+
func (a *analysis) analyze(dir string) error {
118+
for _, analyzer := range a.analyzers() {
119+
analyzer.enterDir(dir)
120+
}
121+
dirents, err := godirwalk.ReadDirents(dir, nil)
122+
if err != nil {
123+
return err
124+
}
125+
126+
var subdirectories []*godirwalk.Dirent
127+
//this is for deterministic results - given the same directory structure
128+
//init should have the same results
129+
sort.Sort(dirents)
130+
131+
// Traverse files
132+
for _, file := range dirents {
133+
if util.IsHiddenFile(file.Name()) || util.IsHiddenDir(file.Name()) {
134+
continue
135+
}
136+
137+
// If we found a directory, keep track of it until we've gone through all the files first
138+
if file.IsDir() {
139+
subdirectories = append(subdirectories, file)
140+
continue
141+
}
142+
143+
filePath := filepath.Join(dir, file.Name())
144+
for _, analyzer := range a.analyzers() {
145+
if err := analyzer.analyzeFile(filePath); err != nil {
146+
return err
147+
}
148+
}
149+
}
150+
151+
// Recurse into subdirectories
152+
for _, subdir := range subdirectories {
153+
if err = a.analyze(filepath.Join(dir, subdir.Name())); err != nil {
154+
return err
155+
}
156+
}
157+
158+
for _, analyzer := range a.analyzers() {
159+
analyzer.exitDir(dir)
160+
}
161+
return nil
162+
}
163+
164+
func (a *analysis) analyzers() []analyzer {
165+
return []analyzer{
166+
a.kubectlAnalyzer,
167+
a.skaffoldAnalyzer,
168+
a.builderAnalyzer,
169+
}
170+
}

pkg/skaffold/initializer/builders.go

-17
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"strings"
2626

2727
"github.com/sirupsen/logrus"
28-
"gopkg.in/AlecAivazis/survey.v1"
2928

3029
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/buildpacks"
3130
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/jib"
@@ -222,19 +221,3 @@ func resolveBuilderImages(builderConfigs []InitBuilder, images []string, force b
222221
}
223222
return pairs, nil
224223
}
225-
226-
func promptUserForBuildConfig(image string, choices []string) (string, error) {
227-
var selectedBuildConfig string
228-
options := append(choices, NoBuilder)
229-
prompt := &survey.Select{
230-
Message: fmt.Sprintf("Choose the builder to build image %s", image),
231-
Options: options,
232-
PageSize: 15,
233-
}
234-
err := survey.AskOne(prompt, &selectedBuildConfig, nil)
235-
if err != nil {
236-
return "", err
237-
}
238-
239-
return selectedBuildConfig, nil
240-
}

pkg/skaffold/initializer/configs.go renamed to pkg/skaffold/initializer/config.go

+9-5
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,17 @@ import (
2323
"strings"
2424

2525
"github.com/sirupsen/logrus"
26-
"gopkg.in/yaml.v2"
2726

2827
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
2928
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/warnings"
3029
)
3130

32-
func generateSkaffoldConfig(k Initializer, buildConfigPairs []builderImagePair) ([]byte, error) {
31+
var (
32+
// for testing
33+
getWd = os.Getwd
34+
)
35+
36+
func generateSkaffoldConfig(k DeploymentInitializer, buildConfigPairs []builderImagePair) *latest.SkaffoldConfig {
3337
// if we're here, the user has no skaffold yaml so we need to generate one
3438
// if the user doesn't have any k8s yamls, generate one for each dockerfile
3539
logrus.Info("generating skaffold config")
@@ -39,7 +43,7 @@ func generateSkaffoldConfig(k Initializer, buildConfigPairs []builderImagePair)
3943
warnings.Printf("Couldn't generate default config name: %s", err.Error())
4044
}
4145

42-
return yaml.Marshal(&latest.SkaffoldConfig{
46+
return &latest.SkaffoldConfig{
4347
APIVersion: latest.Version,
4448
Kind: "Config",
4549
Metadata: latest.Metadata{
@@ -51,11 +55,11 @@ func generateSkaffoldConfig(k Initializer, buildConfigPairs []builderImagePair)
5155
},
5256
Deploy: k.GenerateDeployConfig(),
5357
},
54-
})
58+
}
5559
}
5660

5761
func suggestConfigName() (string, error) {
58-
cwd, err := os.Getwd()
62+
cwd, err := getWd()
5963
if err != nil {
6064
return "", err
6165
}

0 commit comments

Comments
 (0)