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  }