209 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			209 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
| /*
 | |
| Copyright 2018 Google LLC
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package buildcontext
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 
 | |
| 	kConfig "github.com/GoogleContainerTools/kaniko/pkg/config"
 | |
| 	"github.com/go-git/go-billy/v5/osfs"
 | |
| 	"github.com/go-git/go-git/v5"
 | |
| 	"github.com/go-git/go-git/v5/config"
 | |
| 	"github.com/go-git/go-git/v5/plumbing"
 | |
| 	"github.com/go-git/go-git/v5/plumbing/cache"
 | |
| 	"github.com/go-git/go-git/v5/plumbing/transport"
 | |
| 	"github.com/go-git/go-git/v5/plumbing/transport/http"
 | |
| 	"github.com/go-git/go-git/v5/storage/filesystem"
 | |
| 	"github.com/sirupsen/logrus"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	gitPullMethodEnvKey = "GIT_PULL_METHOD"
 | |
| 	gitPullMethodHTTPS  = "https"
 | |
| 	gitPullMethodHTTP   = "http"
 | |
| 
 | |
| 	gitAuthUsernameEnvKey = "GIT_USERNAME"
 | |
| 	gitAuthPasswordEnvKey = "GIT_PASSWORD"
 | |
| 	gitAuthTokenEnvKey    = "GIT_TOKEN"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	supportedGitPullMethods = map[string]bool{gitPullMethodHTTPS: true, gitPullMethodHTTP: true}
 | |
| )
 | |
| 
 | |
| // Git unifies calls to download and unpack the build context.
 | |
| type Git struct {
 | |
| 	context string
 | |
| 	opts    BuildOptions
 | |
| }
 | |
| 
 | |
| // UnpackTarFromBuildContext will provide the directory where Git Repository is Cloned
 | |
| func (g *Git) UnpackTarFromBuildContext() (string, error) {
 | |
| 	directory := kConfig.BuildContextDir
 | |
| 	parts := strings.Split(g.context, "#")
 | |
| 	url := getGitPullMethod() + "://" + parts[0]
 | |
| 	options := git.CloneOptions{
 | |
| 		URL:               url,
 | |
| 		Auth:              getGitAuth(),
 | |
| 		Progress:          os.Stdout,
 | |
| 		SingleBranch:      g.opts.GitSingleBranch,
 | |
| 		RecurseSubmodules: getRecurseSubmodules(g.opts.GitRecurseSubmodules),
 | |
| 	}
 | |
| 	var fetchRef string
 | |
| 	var checkoutRef string
 | |
| 	if len(parts) > 1 {
 | |
| 		if plumbing.IsHash(parts[1]) || !strings.HasPrefix(parts[1], "refs/pull/") {
 | |
| 			// Handle any non-branch refs separately. First, clone the repo HEAD, and
 | |
| 			// then fetch and check out the fetchRef.
 | |
| 			fetchRef = parts[1]
 | |
| 			if plumbing.IsHash(parts[1]) {
 | |
| 				checkoutRef = fetchRef
 | |
| 			} else {
 | |
| 				// The ReferenceName still needs to be present in the options passed
 | |
| 				// to the clone operation for non-hash references of private repositories.
 | |
| 				options.ReferenceName = plumbing.ReferenceName(fetchRef)
 | |
| 			}
 | |
| 		} else {
 | |
| 			// Branches will be cloned directly.
 | |
| 			options.ReferenceName = plumbing.ReferenceName(parts[1])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if branch := g.opts.GitBranch; branch != "" {
 | |
| 		ref, err := getGitReferenceName(directory, url, branch)
 | |
| 		if err != nil {
 | |
| 			return directory, err
 | |
| 		}
 | |
| 		options.ReferenceName = ref
 | |
| 	}
 | |
| 
 | |
| 	logrus.Debugf("Getting source from reference %s", options.ReferenceName)
 | |
| 	r, err := git.PlainClone(directory, false, &options)
 | |
| 	if err != nil {
 | |
| 		return directory, err
 | |
| 	}
 | |
| 
 | |
| 	if fetchRef != "" {
 | |
| 		err = r.Fetch(&git.FetchOptions{
 | |
| 			RemoteName: "origin",
 | |
| 			Auth:       getGitAuth(),
 | |
| 			RefSpecs:   []config.RefSpec{config.RefSpec(fetchRef + ":" + fetchRef)},
 | |
| 		})
 | |
| 		if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
 | |
| 			return directory, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if len(parts) > 2 {
 | |
| 		checkoutRef = parts[2]
 | |
| 	}
 | |
| 	if checkoutRef != "" {
 | |
| 		// ... retrieving the commit being pointed by HEAD
 | |
| 		_, err := r.Head()
 | |
| 		if err != nil {
 | |
| 			return directory, err
 | |
| 		}
 | |
| 
 | |
| 		w, err := r.Worktree()
 | |
| 		if err != nil {
 | |
| 			return directory, err
 | |
| 		}
 | |
| 
 | |
| 		// ... checking out to desired commit
 | |
| 		err = w.Checkout(&git.CheckoutOptions{
 | |
| 			Hash: plumbing.NewHash(checkoutRef),
 | |
| 		})
 | |
| 		if err != nil {
 | |
| 			return directory, err
 | |
| 		}
 | |
| 	}
 | |
| 	return directory, nil
 | |
| }
 | |
| 
 | |
| func getGitReferenceName(directory string, url string, branch string) (plumbing.ReferenceName, error) {
 | |
| 	var remote = git.NewRemote(
 | |
| 		filesystem.NewStorage(
 | |
| 			osfs.New(directory),
 | |
| 			cache.NewObjectLRUDefault(),
 | |
| 		),
 | |
| 		&config.RemoteConfig{
 | |
| 			URLs: []string{url},
 | |
| 		},
 | |
| 	)
 | |
| 
 | |
| 	refs, err := remote.List(&git.ListOptions{
 | |
| 		Auth: getGitAuth(),
 | |
| 	})
 | |
| 	if err != nil {
 | |
| 		return plumbing.HEAD, err
 | |
| 	}
 | |
| 
 | |
| 	if ref := plumbing.NewBranchReferenceName(branch); gitRefExists(ref, refs) {
 | |
| 		return ref, nil
 | |
| 	}
 | |
| 
 | |
| 	if ref := plumbing.NewTagReferenceName(branch); gitRefExists(ref, refs) {
 | |
| 		return ref, nil
 | |
| 	}
 | |
| 
 | |
| 	return plumbing.HEAD, fmt.Errorf("invalid branch: %s", branch)
 | |
| }
 | |
| 
 | |
| func gitRefExists(ref plumbing.ReferenceName, refs []*plumbing.Reference) bool {
 | |
| 	for _, ref2 := range refs {
 | |
| 		if ref.String() == ref2.Name().String() {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func getRecurseSubmodules(v bool) git.SubmoduleRescursivity {
 | |
| 	if v {
 | |
| 		return git.DefaultSubmoduleRecursionDepth
 | |
| 	}
 | |
| 	return git.NoRecurseSubmodules
 | |
| }
 | |
| 
 | |
| func getGitAuth() transport.AuthMethod {
 | |
| 	username := os.Getenv(gitAuthUsernameEnvKey)
 | |
| 	password := os.Getenv(gitAuthPasswordEnvKey)
 | |
| 	token := os.Getenv(gitAuthTokenEnvKey)
 | |
| 	if token != "" {
 | |
| 		username = token
 | |
| 		password = ""
 | |
| 	}
 | |
| 	if username != "" || password != "" {
 | |
| 		return &http.BasicAuth{
 | |
| 			Username: username,
 | |
| 			Password: password,
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func getGitPullMethod() string {
 | |
| 	gitPullMethod := os.Getenv(gitPullMethodEnvKey)
 | |
| 	if ok := supportedGitPullMethods[gitPullMethod]; !ok {
 | |
| 		gitPullMethod = gitPullMethodHTTPS
 | |
| 	}
 | |
| 	return gitPullMethod
 | |
| }
 |