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  }