commit
0c7e1644cb
|
|
@ -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().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.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().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
|
// addHiddenFlags marks certain flags as hidden from the executor help text
|
||||||
|
|
@ -266,7 +267,11 @@ func resolveSourceContext() error {
|
||||||
opts.SrcContext = opts.Bucket
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,12 @@ const (
|
||||||
TarBuildContextPrefix = "tar://"
|
TarBuildContextPrefix = "tar://"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type BuildOptions struct {
|
||||||
|
GitBranch string
|
||||||
|
GitSingleBranch bool
|
||||||
|
GitRecurseSubmodules bool
|
||||||
|
}
|
||||||
|
|
||||||
// BuildContext unifies calls to download and unpack the build context.
|
// BuildContext unifies calls to download and unpack the build context.
|
||||||
type BuildContext interface {
|
type BuildContext interface {
|
||||||
// Unpacks a build context and returns the directory where it resides
|
// Unpacks a build context and returns the directory where it resides
|
||||||
|
|
@ -36,7 +42,7 @@ type BuildContext interface {
|
||||||
|
|
||||||
// GetBuildContext parses srcContext for the prefix and returns related buildcontext
|
// GetBuildContext parses srcContext for the prefix and returns related buildcontext
|
||||||
// parser
|
// parser
|
||||||
func GetBuildContext(srcContext string) (BuildContext, error) {
|
func GetBuildContext(srcContext string, opts BuildOptions) (BuildContext, error) {
|
||||||
split := strings.SplitAfter(srcContext, "://")
|
split := strings.SplitAfter(srcContext, "://")
|
||||||
if len(split) > 1 {
|
if len(split) > 1 {
|
||||||
prefix := split[0]
|
prefix := split[0]
|
||||||
|
|
@ -50,7 +56,7 @@ func GetBuildContext(srcContext string) (BuildContext, error) {
|
||||||
case constants.LocalDirBuildContextPrefix:
|
case constants.LocalDirBuildContextPrefix:
|
||||||
return &Dir{context: context}, nil
|
return &Dir{context: context}, nil
|
||||||
case constants.GitBuildContextPrefix:
|
case constants.GitBuildContextPrefix:
|
||||||
return &Git{context: context}, nil
|
return &Git{context: context, opts: opts}, nil
|
||||||
case constants.HTTPSBuildContextPrefix:
|
case constants.HTTPSBuildContextPrefix:
|
||||||
if util.ValidAzureBlobStorageHost(srcContext) {
|
if util.ValidAzureBlobStorageHost(srcContext) {
|
||||||
return &AzureBlob{context: srcContext}, nil
|
return &AzureBlob{context: srcContext}, nil
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,21 @@ limitations under the License.
|
||||||
package buildcontext
|
package buildcontext
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"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"
|
||||||
|
"github.com/go-git/go-git/v5/config"
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
"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"
|
||||||
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
"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 (
|
const (
|
||||||
|
|
@ -44,21 +51,34 @@ var (
|
||||||
// Git unifies calls to download and unpack the build context.
|
// Git unifies calls to download and unpack the build context.
|
||||||
type Git struct {
|
type Git struct {
|
||||||
context string
|
context string
|
||||||
|
opts BuildOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnpackTarFromBuildContext will provide the directory where Git Repository is Cloned
|
// UnpackTarFromBuildContext will provide the directory where Git Repository is Cloned
|
||||||
func (g *Git) UnpackTarFromBuildContext() (string, error) {
|
func (g *Git) UnpackTarFromBuildContext() (string, error) {
|
||||||
directory := constants.BuildContextDir
|
directory := constants.BuildContextDir
|
||||||
parts := strings.Split(g.context, "#")
|
parts := strings.Split(g.context, "#")
|
||||||
|
url := getGitPullMethod() + "://" + parts[0]
|
||||||
options := git.CloneOptions{
|
options := git.CloneOptions{
|
||||||
URL: getGitPullMethod() + "://" + parts[0],
|
URL: url,
|
||||||
Auth: getGitAuth(),
|
Auth: getGitAuth(),
|
||||||
Progress: os.Stdout,
|
Progress: os.Stdout,
|
||||||
RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
|
SingleBranch: g.opts.GitSingleBranch,
|
||||||
|
RecurseSubmodules: getRecurseSubmodules(g.opts.GitRecurseSubmodules),
|
||||||
}
|
}
|
||||||
if len(parts) > 1 {
|
if len(parts) > 1 {
|
||||||
options.ReferenceName = plumbing.ReferenceName(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)
|
r, err := git.PlainClone(directory, false, &options)
|
||||||
|
|
||||||
if err == nil && len(parts) > 2 {
|
if err == nil && len(parts) > 2 {
|
||||||
|
|
@ -81,10 +101,54 @@ func (g *Git) UnpackTarFromBuildContext() (string, error) {
|
||||||
return directory, err
|
return directory, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
func getGitAuth() transport.AuthMethod {
|
||||||
username := os.Getenv(gitAuthUsernameEnvKey)
|
username := os.Getenv(gitAuthUsernameEnvKey)
|
||||||
password := os.Getenv(gitAuthPasswordEnvKey)
|
password := os.Getenv(gitAuthPasswordEnvKey)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,10 @@ limitations under the License.
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -58,6 +62,47 @@ type KanikoOptions struct {
|
||||||
IgnoreVarRun bool
|
IgnoreVarRun bool
|
||||||
SkipUnusedStages bool
|
SkipUnusedStages bool
|
||||||
RunV2 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.
|
// WarmerOptions are options that are set by command line arguments to the cache warmer.
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue