github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/initializer/build/cli.go (about) 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 build 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "io" 23 "path/filepath" 24 "strings" 25 26 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/buildpacks" 27 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/jib" 28 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" 29 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/initializer/errors" 30 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/generator" 31 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest" 32 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/util" 33 ) 34 35 type cliBuildInitializer struct { 36 cliArtifacts []string 37 artifactInfos []ArtifactInfo 38 manifests []*generator.Container 39 builders []InitBuilder 40 skipBuild bool 41 enableNewFormat bool 42 } 43 44 func (c *cliBuildInitializer) ProcessImages(images []string) error { 45 if len(c.builders) == 0 && len(c.cliArtifacts) == 0 { 46 return errors.NoBuilderErr{} 47 } 48 if err := c.processCliArtifacts(); err != nil { 49 return fmt.Errorf("processing cli artifacts: %w", err) 50 } 51 return nil 52 } 53 54 func (c *cliBuildInitializer) BuildConfig() (latest.BuildConfig, []*latest.PortForwardResource) { 55 pf := []*latest.PortForwardResource{} 56 57 for _, manifestInfo := range c.manifests { 58 // Port value is set to 0 if user decides to not port forward service 59 if manifestInfo.Port != 0 { 60 pf = append(pf, &latest.PortForwardResource{ 61 Type: "service", 62 Name: manifestInfo.Name, 63 Port: util.FromInt(manifestInfo.Port), 64 }) 65 } 66 } 67 68 return latest.BuildConfig{ 69 Artifacts: Artifacts(c.artifactInfos), 70 }, pf 71 } 72 73 func (c *cliBuildInitializer) PrintAnalysis(out io.Writer) error { 74 return printAnalysis(out, c.enableNewFormat, c.skipBuild, c.artifactInfos, c.builders, nil) 75 } 76 77 func (c *cliBuildInitializer) GenerateManifests(_ io.Writer, _, enableManifestGeneration bool) (map[GeneratedArtifactInfo][]byte, error) { 78 generatedManifests := map[GeneratedArtifactInfo][]byte{} 79 80 artifactWithManifests := false 81 for _, info := range c.artifactInfos { 82 if info.Manifest.Generate { 83 artifactWithManifests = true 84 } 85 } 86 if !enableManifestGeneration && !artifactWithManifests { 87 return generatedManifests, nil 88 } 89 for _, info := range c.artifactInfos { 90 if info.Manifest.Generate { 91 generatedInfo := GeneratedArtifactInfo{ 92 ArtifactInfo: info, 93 ManifestPath: filepath.Join(filepath.Dir(info.Builder.Path()), "deployment.yaml"), 94 } 95 manifest, manifestInfo, err := generator.Generate(info.ImageName, info.Manifest.Port) 96 if err != nil { 97 return nil, fmt.Errorf("generating kubernetes manifest: %w", err) 98 } 99 generatedManifests[generatedInfo] = manifest 100 c.manifests = append(c.manifests, manifestInfo) 101 } 102 } 103 return generatedManifests, nil 104 } 105 106 func (c *cliBuildInitializer) processCliArtifacts() error { 107 pairs, err := processCliArtifacts(c.cliArtifacts) 108 if err != nil { 109 return err 110 } 111 c.artifactInfos = pairs 112 return nil 113 } 114 115 func processCliArtifacts(cliArtifacts []string) ([]ArtifactInfo, error) { 116 var artifactInfos []ArtifactInfo 117 for _, artifact := range cliArtifacts { 118 // Parses artifacts in 1 of 2 forms: 119 // 1. JSON in the form of: {"builder":"Name of Builder","payload":{...},"image":"image.name","context":"artifact.context"}. 120 // The builder field is parsed first to determine the builder type, and the payload is parsed 121 // afterwards once the type is determined. 122 // 2. Key-value pair: `path/to/Dockerfile=imageName` (deprecated, historical, Docker-only) 123 a := struct { 124 Name string `json:"builder"` 125 Image string `json:"image"` 126 Workspace string `json:"context"` 127 Manifest struct { 128 Generate bool `json:"generate"` 129 Port int `json:"port"` 130 } `json:"manifest"` 131 }{} 132 if err := json.Unmarshal([]byte(artifact), &a); err != nil { 133 // Not JSON, use backwards compatible method 134 parts := strings.Split(artifact, "=") 135 if len(parts) != 2 { 136 return nil, fmt.Errorf("malformed artifact provided: %s", artifact) 137 } 138 artifactInfos = append(artifactInfos, ArtifactInfo{ 139 Builder: docker.ArtifactConfig{File: parts[0]}, 140 ImageName: parts[1], 141 }) 142 continue 143 } 144 manifestInfo := ManifestInfo{ 145 Generate: a.Manifest.Generate, 146 Port: a.Manifest.Port, 147 } 148 149 // Use builder type to parse payload 150 switch a.Name { 151 case docker.Name: 152 parsed := struct { 153 Payload docker.ArtifactConfig `json:"payload"` 154 }{} 155 if err := json.Unmarshal([]byte(artifact), &parsed); err != nil { 156 return nil, err 157 } 158 info := ArtifactInfo{Builder: parsed.Payload, ImageName: a.Image, Workspace: a.Workspace, Manifest: manifestInfo} 159 artifactInfos = append(artifactInfos, info) 160 161 // FIXME: shouldn't use a human-readable name? 162 case jib.PluginName(jib.JibGradle), jib.PluginName(jib.JibMaven): 163 parsed := struct { 164 Payload jib.ArtifactConfig `json:"payload"` 165 }{} 166 if err := json.Unmarshal([]byte(artifact), &parsed); err != nil { 167 return nil, err 168 } 169 parsed.Payload.BuilderName = a.Name 170 info := ArtifactInfo{Builder: parsed.Payload, ImageName: a.Image, Workspace: a.Workspace, Manifest: manifestInfo} 171 artifactInfos = append(artifactInfos, info) 172 173 case buildpacks.Name: 174 parsed := struct { 175 Payload buildpacks.ArtifactConfig `json:"payload"` 176 }{} 177 if err := json.Unmarshal([]byte(artifact), &parsed); err != nil { 178 return nil, err 179 } 180 info := ArtifactInfo{Builder: parsed.Payload, ImageName: a.Image, Workspace: a.Workspace, Manifest: manifestInfo} 181 artifactInfos = append(artifactInfos, info) 182 183 case "None": 184 info := ArtifactInfo{Builder: NoneBuilder{}, ImageName: a.Image, Manifest: manifestInfo} 185 artifactInfos = append(artifactInfos, info) 186 187 default: 188 return nil, fmt.Errorf("unknown builder type in CLI artifacts: %q", a.Name) 189 } 190 } 191 return artifactInfos, nil 192 }