Skip to content

Commit

Permalink
cleanup on analyzers and moving things into a single package (GoogleC…
Browse files Browse the repository at this point in the history
  • Loading branch information
balopat authored and dgageot committed Jan 21, 2020
1 parent 586da02 commit 8d56bcb
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 112 deletions.
114 changes: 31 additions & 83 deletions pkg/skaffold/initializer/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,11 @@ limitations under the License.
package initializer

import (
"fmt"
"path/filepath"
"sort"

"github.com/karrick/godirwalk"
"github.com/sirupsen/logrus"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/initializer/kubectl"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
)

Expand All @@ -34,86 +31,9 @@ type analysis struct {
builderAnalyzer *builderAnalyzer
}

// analyzer is a generic Visitor that is called on every file in the directory
// It can manage state and react to walking events assuming a bread first search
type analyzer interface {
enterDir(dir string)
analyzeFile(file string) error
exitDir(dir string)
}

type directoryAnalyzer struct {
currentDir string
}

func (a *directoryAnalyzer) analyzeFile(filePath string) error {
return nil
}

func (a *directoryAnalyzer) enterDir(dir string) {
a.currentDir = dir
}

func (a *directoryAnalyzer) exitDir(dir string) {
//pass
}

type kubectlAnalyzer struct {
directoryAnalyzer
kubernetesManifests []string
}

func (a *kubectlAnalyzer) analyzeFile(filePath string) error {
if kubectl.IsKubernetesManifest(filePath) && !IsSkaffoldConfig(filePath) {
a.kubernetesManifests = append(a.kubernetesManifests, filePath)
}
return nil
}

type skaffoldConfigAnalyzer struct {
directoryAnalyzer
force bool
}

func (a *skaffoldConfigAnalyzer) analyzeFile(filePath string) error {
if !IsSkaffoldConfig(filePath) {
return nil
}
if !a.force {
return fmt.Errorf("pre-existing %s found (you may continue with --force)", filePath)
}
logrus.Debugf("%s is a valid skaffold configuration: continuing since --force=true", filePath)
return nil
}

type builderAnalyzer struct {
directoryAnalyzer
enableJibInit bool
enableBuildpackInit bool
findBuilders bool
foundBuilders []InitBuilder

parentDirToStopFindBuilders string
}

func (a *builderAnalyzer) analyzeFile(filePath string) error {
if a.findBuilders && (a.parentDirToStopFindBuilders == "" || a.parentDirToStopFindBuilders == a.currentDir) {
builderConfigs, continueSearchingBuilders := detectBuilders(a.enableJibInit, a.enableBuildpackInit, filePath)
a.foundBuilders = append(a.foundBuilders, builderConfigs...)
if !continueSearchingBuilders {
a.parentDirToStopFindBuilders = a.currentDir
}
}
return nil
}

func (a *builderAnalyzer) exitDir(dir string) {
if a.parentDirToStopFindBuilders == dir {
a.parentDirToStopFindBuilders = ""
}
}

// analyze recursively walks a directory and returns the k8s configs and builder configs that it finds
// analyze recursively walks a directory and notifies the analyzers of files and enterDir and exitDir events
// at the end of the analyze function the analysis struct's analyzers should contain the state that we can
// use to do further computation.
func (a *analysis) analyze(dir string) error {
for _, analyzer := range a.analyzers() {
analyzer.enterDir(dir)
Expand Down Expand Up @@ -168,3 +88,31 @@ func (a *analysis) analyzers() []analyzer {
a.builderAnalyzer,
}
}

// analyzer is following the visitor pattern. It is called on every file
// as the analysis.analyze function walks the directory structure recursively.
// It can manage state and react to walking events assuming a breadth first search.
type analyzer interface {
enterDir(dir string)
analyzeFile(file string) error
exitDir(dir string)
}

// directoryAnalyzer is a base analyzer that can be included in every analyzer as a convenience
// it saves the current directory on enterDir events. Benefits to include this into other analyzers is that
// they can rely on the current directory var, but also they don't have to implement enterDir and exitDir.
type directoryAnalyzer struct {
currentDir string
}

func (a *directoryAnalyzer) analyzeFile(filePath string) error {
return nil
}

func (a *directoryAnalyzer) enterDir(dir string) {
a.currentDir = dir
}

func (a *directoryAnalyzer) exitDir(dir string) {
//pass
}
27 changes: 27 additions & 0 deletions pkg/skaffold/initializer/builders.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,33 @@ import (
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
)

type builderAnalyzer struct {
directoryAnalyzer
enableJibInit bool
enableBuildpackInit bool
findBuilders bool
foundBuilders []InitBuilder

parentDirToStopFindBuilders string
}

func (a *builderAnalyzer) analyzeFile(filePath string) error {
if a.findBuilders && (a.parentDirToStopFindBuilders == "" || a.parentDirToStopFindBuilders == a.currentDir) {
builderConfigs, continueSearchingBuilders := detectBuilders(a.enableJibInit, a.enableBuildpackInit, filePath)
a.foundBuilders = append(a.foundBuilders, builderConfigs...)
if !continueSearchingBuilders {
a.parentDirToStopFindBuilders = a.currentDir
}
}
return nil
}

func (a *builderAnalyzer) exitDir(dir string) {
if a.parentDirToStopFindBuilders == dir {
a.parentDirToStopFindBuilders = ""
}
}

// autoSelectBuilders takes a list of builders and images, checks if any of the builders' configured target
// images match an image in the image list, and returns a list of the matching builder/image pairs. Also
// separately returns the builder configs and images that didn't have any matches.
Expand Down
21 changes: 19 additions & 2 deletions pkg/skaffold/initializer/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package initializer

import (
"fmt"
"os"
"path/filepath"
"regexp"
Expand All @@ -33,7 +34,23 @@ var (
getWd = os.Getwd
)

func generateSkaffoldConfig(k DeploymentInitializer, buildConfigPairs []builderImagePair) *latest.SkaffoldConfig {
type skaffoldConfigAnalyzer struct {
directoryAnalyzer
force bool
}

func (a *skaffoldConfigAnalyzer) analyzeFile(filePath string) error {
if !isSkaffoldConfig(filePath) {
return nil
}
if !a.force {
return fmt.Errorf("pre-existing %s found (you may continue with --force)", filePath)
}
logrus.Debugf("%s is a valid skaffold configuration: continuing since --force=true", filePath)
return nil
}

func generateSkaffoldConfig(k deploymentInitializer, buildConfigPairs []builderImagePair) *latest.SkaffoldConfig {
// if we're here, the user has no skaffold yaml so we need to generate one
// if the user doesn't have any k8s yamls, generate one for each dockerfile
logrus.Info("generating skaffold config")
Expand All @@ -53,7 +70,7 @@ func generateSkaffoldConfig(k DeploymentInitializer, buildConfigPairs []builderI
Build: latest.BuildConfig{
Artifacts: artifacts(buildConfigPairs),
},
Deploy: k.GenerateDeployConfig(),
Deploy: k.deployConfig(),
},
}
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/skaffold/initializer/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ import (
)

type stubDeploymentInitializer struct {
deployConfig latest.DeployConfig
config latest.DeployConfig
}

func (s stubDeploymentInitializer) GenerateDeployConfig() latest.DeployConfig {
return s.deployConfig
func (s stubDeploymentInitializer) deployConfig() latest.DeployConfig {
return s.config
}

func (s stubDeploymentInitializer) GetImages() []string {
Expand Down
11 changes: 5 additions & 6 deletions pkg/skaffold/initializer/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (
"github.com/GoogleContainerTools/skaffold/cmd/skaffold/app/tips"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/initializer/kubectl"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/warnings"
)
Expand All @@ -40,10 +39,10 @@ import (
// an image we parse out from a Kubernetes manifest
const NoBuilder = "None (image not built from these sources)"

// DeploymentInitializer detects a deployment type and is able to extract image names from it
type DeploymentInitializer interface {
// GenerateDeployConfig generates Deploy Config for skaffold configuration.
GenerateDeployConfig() latest.DeployConfig
// deploymentInitializer detects a deployment type and is able to extract image names from it
type deploymentInitializer interface {
// deployConfig generates Deploy Config for skaffold configuration.
deployConfig() latest.DeployConfig
// GetImages fetches all the images defined in the manifest files.
GetImages() []string
}
Expand Down Expand Up @@ -108,7 +107,7 @@ func DoInit(ctx context.Context, out io.Writer, c Config) error {
return err
}

k, err := kubectl.New(a.kubectlAnalyzer.kubernetesManifests)
k, err := newKubectlInitializer(a.kubectlAnalyzer.kubernetesManifests)
if err != nil {
return err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package kubectl
package initializer

import (
"bufio"
Expand All @@ -32,29 +32,39 @@ import (

var requiredFields = []string{"apiVersion", "kind", "metadata"}

// Kubectl holds parameters to run kubectl.
type Kubectl struct {
// kubectl implements deploymentInitializer for the kubectl deployer.
type kubectl struct {
configs []string
images []string
}

// New returns a Kubectl skaffold generator.
func New(potentialConfigs []string) (*Kubectl, error) {
// kubectlAnalyzer is a Visitor during the directory analysis that collects kubernetes manifests
type kubectlAnalyzer struct {
directoryAnalyzer
kubernetesManifests []string
}

func (a *kubectlAnalyzer) analyzeFile(filePath string) error {
if IsKubernetesManifest(filePath) && !isSkaffoldConfig(filePath) {
a.kubernetesManifests = append(a.kubernetesManifests, filePath)
}
return nil
}

// newKubectlInitializer returns a kubectl skaffold generator.
func newKubectlInitializer(potentialConfigs []string) (*kubectl, error) {
var k8sConfigs, images []string
for _, file := range potentialConfigs {
imgs, err := parseImagesFromKubernetesYaml(file)
if err == nil {
logrus.Infof("found valid k8s yaml: %s", file)
k8sConfigs = append(k8sConfigs, file)
images = append(images, imgs...)
} else {
logrus.Infof("invalid k8s yaml %s: %s", file, err.Error())
}
}
if len(k8sConfigs) == 0 {
return nil, errors.New("one or more valid Kubernetes manifests is required to run skaffold")
}
return &Kubectl{
return &kubectl{
configs: k8sConfigs,
images: images,
}, nil
Expand All @@ -70,9 +80,9 @@ func IsKubernetesManifest(file string) bool {
return err == nil
}

// GenerateDeployConfig implements the Initializer interface and generates
// deployConfig implements the Initializer interface and generates
// skaffold kubectl deployment config.
func (k *Kubectl) GenerateDeployConfig() latest.DeployConfig {
func (k *kubectl) deployConfig() latest.DeployConfig {
return latest.DeployConfig{
DeployType: latest.DeployType{
KubectlDeploy: &latest.KubectlDeploy{
Expand All @@ -84,7 +94,7 @@ func (k *Kubectl) GenerateDeployConfig() latest.DeployConfig {

// GetImages implements the Initializer interface and lists all the
// images present in the k8 manifest files.
func (k *Kubectl) GetImages() []string {
func (k *kubectl) GetImages() []string {
return k.images
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package kubectl
package initializer

import (
"testing"
Expand All @@ -38,7 +38,7 @@ spec:
`)
filename := tmpDir.Path("deployment.yaml")

k, err := New([]string{filename})
k, err := newKubectlInitializer([]string{filename})
if err != nil {
t.Fatal("failed to create a pipeline")
}
Expand All @@ -50,7 +50,7 @@ spec:
},
},
}
testutil.CheckDeepEqual(t, expectedConfig, k.GenerateDeployConfig())
testutil.CheckDeepEqual(t, expectedConfig, k.deployConfig())
}

func TestParseImagesFromKubernetesYaml(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions pkg/skaffold/initializer/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ package initializer

import "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema"

// IsSkaffoldConfig is for determining if a file is skaffold config file.
func IsSkaffoldConfig(file string) bool {
// isSkaffoldConfig is for determining if a file is skaffold config file.
func isSkaffoldConfig(file string) bool {
if config, err := schema.ParseConfig(file, false); err == nil && config != nil {
return true
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/skaffold/initializer/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ deploy:
tmpDir := t.NewTempDir().
Write("skaffold.yaml", test.contents)

isValid := IsSkaffoldConfig(tmpDir.Path("skaffold.yaml"))
isValid := isSkaffoldConfig(tmpDir.Path("skaffold.yaml"))

t.CheckDeepEqual(test.isValid, isValid)
})
Expand Down

0 comments on commit 8d56bcb

Please sign in to comment.