Skip to content

Commit

Permalink
Merge pull request #1322 from caarlos0/opts
Browse files Browse the repository at this point in the history
feat: new git flags
  • Loading branch information
tejal29 authored Aug 13, 2020
2 parents bfd92b5 + df7f4cc commit 0c7e164
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 7 deletions.
7 changes: 6 additions & 1 deletion cmd/executor/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ func addKanikoOptionsFlags() {
RootCmd.PersistentFlags().VarP(&opts.Labels, "label", "", "Set metadata for an image. Set it repeatedly for multiple labels.")
RootCmd.PersistentFlags().BoolVarP(&opts.SkipUnusedStages, "skip-unused-stages", "", false, "Build only used stages if defined to true. Otherwise it builds by default all stages, even the unnecessaries ones until it reaches the target stage / end of Dockerfile")
RootCmd.PersistentFlags().BoolVarP(&opts.RunV2, "use-new-run", "", false, "Experimental run command to detect file system changes. This new run command does no rely on snapshotting to detect changes.")
RootCmd.PersistentFlags().Var(&opts.Git, "git", "Branch to clone if build context is a git repository")
}

// addHiddenFlags marks certain flags as hidden from the executor help text
Expand Down Expand Up @@ -266,7 +267,11 @@ func resolveSourceContext() error {
opts.SrcContext = opts.Bucket
}
}
contextExecutor, err := buildcontext.GetBuildContext(opts.SrcContext)
contextExecutor, err := buildcontext.GetBuildContext(opts.SrcContext, buildcontext.BuildOptions{
GitBranch: opts.Git.Branch,
GitSingleBranch: opts.Git.SingleBranch,
GitRecurseSubmodules: opts.Git.RecurseSubmodules,
})
if err != nil {
return err
}
Expand Down
10 changes: 8 additions & 2 deletions pkg/buildcontext/buildcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ const (
TarBuildContextPrefix = "tar://"
)

type BuildOptions struct {
GitBranch string
GitSingleBranch bool
GitRecurseSubmodules bool
}

// BuildContext unifies calls to download and unpack the build context.
type BuildContext interface {
// Unpacks a build context and returns the directory where it resides
Expand All @@ -36,7 +42,7 @@ type BuildContext interface {

// GetBuildContext parses srcContext for the prefix and returns related buildcontext
// parser
func GetBuildContext(srcContext string) (BuildContext, error) {
func GetBuildContext(srcContext string, opts BuildOptions) (BuildContext, error) {
split := strings.SplitAfter(srcContext, "://")
if len(split) > 1 {
prefix := split[0]
Expand All @@ -50,7 +56,7 @@ func GetBuildContext(srcContext string) (BuildContext, error) {
case constants.LocalDirBuildContextPrefix:
return &Dir{context: context}, nil
case constants.GitBuildContextPrefix:
return &Git{context: context}, nil
return &Git{context: context, opts: opts}, nil
case constants.HTTPSBuildContextPrefix:
if util.ValidAzureBlobStorageHost(srcContext) {
return &AzureBlob{context: srcContext}, nil
Expand Down
72 changes: 68 additions & 4 deletions pkg/buildcontext/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,21 @@ limitations under the License.
package buildcontext

import (
"fmt"
"os"
"strings"

"github.com/GoogleContainerTools/kaniko/pkg/constants"
"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"

"github.com/GoogleContainerTools/kaniko/pkg/constants"
)

const (
Expand All @@ -44,21 +51,34 @@ var (
// 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 := constants.BuildContextDir
parts := strings.Split(g.context, "#")
url := getGitPullMethod() + "://" + parts[0]
options := git.CloneOptions{
URL: getGitPullMethod() + "://" + parts[0],
URL: url,
Auth: getGitAuth(),
Progress: os.Stdout,
RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
SingleBranch: g.opts.GitSingleBranch,
RecurseSubmodules: getRecurseSubmodules(g.opts.GitRecurseSubmodules),
}
if len(parts) > 1 {
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 && len(parts) > 2 {
Expand All @@ -81,10 +101,54 @@ func (g *Git) UnpackTarFromBuildContext() (string, error) {
return directory, err
}
}

return directory, err
}

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)
Expand Down
45 changes: 45 additions & 0 deletions pkg/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ limitations under the License.
package config

import (
"errors"
"fmt"
"strconv"
"strings"
"time"
)

Expand Down Expand Up @@ -58,6 +62,47 @@ type KanikoOptions struct {
IgnoreVarRun bool
SkipUnusedStages bool
RunV2 bool
Git KanikoGitOptions
}

type KanikoGitOptions struct {
Branch string
SingleBranch bool
RecurseSubmodules bool
}

var ErrInvalidGitFlag = errors.New("invalid git flag, must be in the key=value format")

func (k *KanikoGitOptions) Type() string {
return "gitoptions"
}

func (k *KanikoGitOptions) String() string {
return fmt.Sprintf("branch=%s,single-branch=%t,recurse-submodules=%t", k.Branch, k.SingleBranch, k.RecurseSubmodules)
}

func (k *KanikoGitOptions) Set(s string) error {
var parts = strings.SplitN(s, "=", 2)
if len(parts) != 2 {
return fmt.Errorf("%w: %s", ErrInvalidGitFlag, s)
}
switch parts[0] {
case "branch":
k.Branch = parts[1]
case "single-branch":
v, err := strconv.ParseBool(parts[1])
if err != nil {
return err
}
k.SingleBranch = v
case "recurse-submodules":
v, err := strconv.ParseBool(parts[1])
if err != nil {
return err
}
k.RecurseSubmodules = v
}
return nil
}

// WarmerOptions are options that are set by command line arguments to the cache warmer.
Expand Down
53 changes: 53 additions & 0 deletions pkg/config/options_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
Copyright 2020 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 config

import (
"testing"

"github.com/GoogleContainerTools/kaniko/testutil"
)

func TestKanikoGitOptions(t *testing.T) {
t.Run("invalid pair", func(t *testing.T) {
var g = &KanikoGitOptions{}
testutil.CheckError(t, true, g.Set("branch"))
})

t.Run("sets values", func(t *testing.T) {
var g = &KanikoGitOptions{}
testutil.CheckNoError(t, g.Set("branch=foo"))
testutil.CheckNoError(t, g.Set("recurse-submodules=true"))
testutil.CheckNoError(t, g.Set("single-branch=true"))
testutil.CheckDeepEqual(t, KanikoGitOptions{
Branch: "foo",
SingleBranch: true,
RecurseSubmodules: true,
}, *g)
})

t.Run("sets bools other than true", func(t *testing.T) {
var g = KanikoGitOptions{}
testutil.CheckError(t, true, g.Set("recurse-submodules="))
testutil.CheckError(t, true, g.Set("single-branch=zaza"))
testutil.CheckNoError(t, g.Set("recurse-submodules=false"))
testutil.CheckDeepEqual(t, KanikoGitOptions{
SingleBranch: false,
RecurseSubmodules: false,
}, g)
})
}

0 comments on commit 0c7e164

Please sign in to comment.