github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/build/jib/init.go (about) 1 /* 2 Copyright 2019 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 jib 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/json" 23 "fmt" 24 "io/ioutil" 25 "os/exec" 26 "path/filepath" 27 "regexp" 28 "strings" 29 30 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log" 31 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" 32 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util" 33 ) 34 35 // For testing 36 var ( 37 Validate = validate 38 ) 39 40 // ArtifactConfig holds information about a Jib project 41 type ArtifactConfig struct { 42 BuilderName string `json:"-"` 43 Image string `json:"image,omitempty"` 44 File string `json:"path,omitempty"` 45 Project string `json:"project,omitempty"` 46 } 47 48 // Name returns the name of the builder 49 func (c ArtifactConfig) Name() string { 50 return c.BuilderName 51 } 52 53 // Describe returns the initBuilder's string representation, used when prompting the user to choose a builder. 54 func (c ArtifactConfig) Describe() string { 55 if c.Project != "" { 56 return fmt.Sprintf("%s (%s, %s)", c.BuilderName, c.Project, c.File) 57 } 58 return fmt.Sprintf("%s (%s)", c.BuilderName, c.File) 59 } 60 61 // ArtifactType returns the type of the artifact to be built. 62 func (c ArtifactConfig) ArtifactType(_ string) latest.ArtifactType { 63 return latest.ArtifactType{ 64 JibArtifact: &latest.JibArtifact{ 65 Project: c.Project, 66 }, 67 } 68 } 69 70 // ConfiguredImage returns the target image configured by the builder, or empty string if no image is configured 71 func (c ArtifactConfig) ConfiguredImage() string { 72 return c.Image 73 } 74 75 // Path returns the path to the build definition 76 func (c ArtifactConfig) Path() string { 77 return c.File 78 } 79 80 // BuilderConfig contains information about inferred Jib configurations 81 type jibJSON struct { 82 Image string `json:"image"` 83 Project string `json:"project"` 84 } 85 86 // validate checks if a file is a valid Jib configuration. Returns the list of Config objects corresponding to each Jib project built by the file, or nil if Jib is not configured. 87 func validate(ctx context.Context, path string, enableGradleAnalysis bool) []ArtifactConfig { 88 if !JVMFound(ctx) { 89 log.Entry(context.TODO()).Debugf("Skipping Jib for init for %q: no functioning Java VM", path) 90 return nil 91 } 92 // Determine whether maven or gradle 93 var builderType PluginType 94 var executable, wrapper, taskName, searchString, consoleFlag string 95 switch { 96 case isPomFile(path): 97 builderType = JibMaven 98 executable = "mvn" 99 wrapper = "mvnw" 100 searchString = "jib-maven-plugin" 101 taskName = "jib:_skaffold-init" 102 consoleFlag = "--batch-mode" 103 104 case enableGradleAnalysis && (strings.HasSuffix(path, "build.gradle") || strings.HasSuffix(path, "build.gradle.kts")): 105 builderType = JibGradle 106 executable = "gradle" 107 wrapper = "gradlew" 108 searchString = "com.google.cloud.tools.jib" 109 taskName = "_jibSkaffoldInit" 110 consoleFlag = "--console=plain" 111 default: 112 return nil 113 } 114 115 // Search for indication of Jib in build file before proceeding 116 if content, err := ioutil.ReadFile(path); err != nil || !strings.Contains(string(content), searchString) { 117 return nil 118 } 119 120 // Run Jib's skaffold init task/goal to check if Jib is configured 121 if wrapperExecutable, err := util.AbsFile(filepath.Dir(path), wrapper); err == nil { 122 executable = wrapperExecutable 123 } 124 cmd := exec.Command(executable, taskName, "-q", consoleFlag) 125 cmd.Dir = filepath.Dir(path) 126 stdout, err := util.RunCmdOut(ctx, cmd) 127 if err != nil { 128 return nil 129 } 130 131 // Parse Jib output. Multiple JSON strings may be printed for multi-project/multi-module setups. 132 matches := regexp.MustCompile(`BEGIN JIB JSON\r?\n({.*})`).FindAllSubmatch(stdout, -1) 133 if len(matches) == 0 { 134 return nil 135 } 136 137 var results []ArtifactConfig 138 for _, match := range matches { 139 // Escape windows path separators 140 line := bytes.ReplaceAll(match[1], []byte(`\`), []byte(`\\`)) 141 parsedJSON := jibJSON{} 142 if err := json.Unmarshal(line, &parsedJSON); err != nil { 143 log.Entry(context.TODO()).Warnf("failed to parse jib json: %s", err.Error()) 144 return nil 145 } 146 147 results = append(results, ArtifactConfig{ 148 BuilderName: PluginName(builderType), 149 Image: parsedJSON.Image, 150 File: path, 151 Project: parsedJSON.Project, 152 }) 153 } 154 return results 155 } 156 157 // checks that the file is a maven pom file, and returns the file extension 158 func isPomFile(path string) bool { 159 filename := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path)) 160 return filename == "pom" 161 }