Skip to content

Commit

Permalink
Merge pull request #23 from cedrickring/remove-external-dependencies
Browse files Browse the repository at this point in the history
Remove external dependencies
  • Loading branch information
cedrickring authored Dec 30, 2018
2 parents aa65032 + d476c17 commit b0df4e6
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 35 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ Specify namespace for the builder to run in (defaults to "default" namespace)
This flag allows you to pass in build args (ARG) for the Kaniko executor
### Registry credentials
You can either have your Docker Container Registry credentials in your `~/.docker/config.json` or provide them with the
`--username` (`-u`) and `--password` (`-p`) flags.
When using the cli flags, the container registry url is guessed based on the first provided image tag.
e.g. `-t my.registry.com/tag` is guessed as `my.registry.com`. If no specific registry is provided in the tag, it defaults to
`https://index.docker.io/v1/`.
### How does kbuild work?
In order to use the local context, the context needs to be tar-ed, copied to an Init Container, which shares an
Expand Down
52 changes: 43 additions & 9 deletions cmd/kbuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ package main

import (
"github.com/Sirupsen/logrus"
"github.com/cedrickring/kbuild/pkg/docker"
"github.com/cedrickring/kbuild/pkg/kaniko"
"github.com/google/go-containerregistry/pkg/name"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"k8s.io/api/core/v1"
"os"
"path/filepath"
)
Expand All @@ -34,6 +36,8 @@ var (
imageTags []string
buildArgs []string
useCache bool
username string
password string
)

func main() {
Expand All @@ -47,6 +51,8 @@ func main() {
rootCmd.Flags().StringVarP(&workingDir, "workdir", "w", ".", "Working directory")
rootCmd.Flags().StringVarP(&namespace, "namespace", "n", "default", "The namespace to run the build in")
rootCmd.Flags().StringVarP(&cacheRepo, "cache-repo", "", "", "Repository for cached images (see --cache)")
rootCmd.Flags().StringVarP(&username, "username", "u", "", "Docker Registry username")
rootCmd.Flags().StringVarP(&password, "password", "p", "", "Docker Registry password")
rootCmd.Flags().StringSliceVarP(&imageTags, "tag", "t", nil, "Final image tag(s) (required)")
rootCmd.Flags().StringSliceVarP(&buildArgs, "build-arg", "", nil, "Optional build arguments (ARG)")
rootCmd.Flags().BoolVarP(&useCache, "cache", "c", false, "Enable RUN command caching")
Expand All @@ -59,14 +65,18 @@ func run(_ *cobra.Command, _ []string) {
setupLogrus()

if err := validateImageTags(); err != nil {
logrus.Error(err)
os.Exit(1)
logrus.Fatal(err)
return
}

if err := checkForDockerfile(); err != nil {
logrus.Error(err)
os.Exit(1)
logrus.Fatal(err)
return
}

credentialsMap, err := getCredentialsConfigMap()
if err != nil {
logrus.Fatal(err)
return
}

Expand All @@ -87,15 +97,15 @@ func run(_ *cobra.Command, _ []string) {
CacheRepo: cacheRepo,
Namespace: namespace,
BuildArgs: buildArgs,
CredentialsMap: credentialsMap,
}
err := b.StartBuild()
err = b.StartBuild()
if err != nil {
if err == kaniko.ErrorBuildFailed {
logrus.Error("Build failed.")
logrus.Fatal("Build failed.")
} else {
logrus.Error(err)
logrus.Fatal(err)
}
os.Exit(1)
}
}

Expand All @@ -121,4 +131,28 @@ func setupLogrus() {
ForceColors: true,
})
logrus.SetOutput(os.Stdout)
}
}

func getCredentialsConfigMap() (*v1.ConfigMap, error) {
var credentials []byte

//check if credentials have been provided by flags
if username != "" && password != "" {
logrus.Infoln("Using credentials from flags")
registry := docker.GuessRegistryFromTag(imageTags[0])
creds, err := docker.GetCredentialsFromFlags(username, password, registry)
if err != nil {
return nil, errors.Wrap(err, "getting credentials from flags")
}
credentials = creds
} else { //otherwise read ~/.docker/config.json
logrus.Infoln("Using credentials from ~/.docker/config.json")
creds, err := docker.GetCredentialsFromConfig()
if err != nil {
return nil, errors.Wrap(err, "getting credentials from config")
}
credentials = creds
}

return docker.GetCredentialsAsConfigMap(credentials), nil
}
7 changes: 6 additions & 1 deletion pkg/docker/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,16 @@ func CreateContextFromWorkingDir(workDir, dockerfile string, w io.Writer, buildA
continue
}

rel, err := filepath.Rel(workDir, path) //make path relative to work dir
if err != nil {
return err
}

header, err := tar.FileInfoHeader(info, info.Name())
if err != nil {
return errors.Wrap(err, "creating tar file info header")
}
header.Name = strings.Replace(path, "\\", "/", -1)
header.Name = strings.Replace(rel, "\\", "/", -1)

if err := copyFile(header, path, tw); err != nil {
return err
Expand Down
67 changes: 50 additions & 17 deletions pkg/docker/config.go → pkg/docker/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
package docker

import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/cedrickring/kbuild/pkg/constants"
"github.com/cedrickring/kbuild/pkg/util"
"github.com/pkg/errors"
Expand All @@ -25,30 +28,45 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"os"
"path/filepath"
"regexp"
"strings"
)

//GetConfigAsConfigMap gets the local .docker/config.json in a ConfigMap
func GetConfigAsConfigMap() (*v1.ConfigMap, error) {
config, err := GetConfig()
if err != nil {
return nil, err
type dockerConfig struct {
Auths map[string]auth `json:"auths"`
}

type auth struct {
Auth string `json:"auth"`
}

var registryRegex, _ = regexp.Compile("^((.*)\\.)?(.*)\\.(.*)/")

//GuessRegistryFromTag guesses the container registry based on the provided image tag.
//Defaults to the index.docker.io registry
func GuessRegistryFromTag(imageTag string) string {
if registryRegex.MatchString(imageTag) {
return imageTag[:strings.Index(imageTag, "/")]
}

return &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.ConfigMapName,
Labels: map[string]string{
"builder": "kaniko",
},
},
Data: map[string]string{
"config.json": string(config),
return "https://index.docker.io/v1/"
}

//GetCredentialsFromFlags creates a dockerconfig "auths" object containing the provided credentials
func GetCredentialsFromFlags(username, password, registry string) ([]byte, error) {
encoded := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))

config := dockerConfig{
Auths: map[string]auth{
registry: {Auth: encoded},
},
}, nil
}

return json.Marshal(config)
}

//GetConfig reads the docker config located at ~/.docker/config.json
func GetConfig() ([]byte, error) {
//GetCredentialsFromConfig reads the docker config located at ~/.docker/config.json
func GetCredentialsFromConfig() ([]byte, error) {
home := util.HomeDir()
if home == "" {
return nil, errors.New("Can't find docker config at ~/.docker/config.json")
Expand All @@ -61,3 +79,18 @@ func GetConfig() ([]byte, error) {

return ioutil.ReadFile(dockerConfigPath)
}

//GetCredentialsAsConfigMap creates a new v1.ConfigMap with the provided credentials
func GetCredentialsAsConfigMap(credentials []byte) *v1.ConfigMap {
return &v1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: constants.ConfigMapName,
Labels: map[string]string{
"builder": "kaniko",
},
},
Data: map[string]string{
"config.json": string(credentials),
},
}
}
48 changes: 48 additions & 0 deletions pkg/docker/credentials_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
Copyright 2018 Cedric Kring
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 docker

import (
"testing"
)

func TestGuessRegistryFromTag(t *testing.T) {
tests := []struct {
imageTag string
expectedRegistry string
}{
{
imageTag: "my.registry.com/tag:latest",
expectedRegistry: "my.registry.com",
},
{
imageTag: "registry.com/tag:latest",
expectedRegistry: "registry.com",
},
{
imageTag: "openjdk:latest",
expectedRegistry: "https://index.docker.io/v1/",
},
}

for _, test := range tests {
registry := GuessRegistryFromTag(test.imageTag)
if registry != test.expectedRegistry {
t.Errorf("Expected %s but got %s\n", test.expectedRegistry, registry)
}
}
}
12 changes: 4 additions & 8 deletions pkg/kaniko/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type Build struct {
CacheRepo string
Namespace string
BuildArgs []string
CredentialsMap *v1.ConfigMap

tarPath string
}
Expand Down Expand Up @@ -104,21 +105,16 @@ func (b Build) StartBuild() error {
}

func (b Build) checkForConfigMap(client *k8s.Clientset) error {
configMap, err := docker.GetConfigAsConfigMap()
if err != nil {
return errors.Wrap(err, "get docker config as ConfigMap")
}

configMaps := client.CoreV1().ConfigMaps(b.Namespace)

_, err = configMaps.Get(configMap.Name, metav1.GetOptions{})
_, err := configMaps.Get(b.CredentialsMap.Name, metav1.GetOptions{})
if err != nil { //configmap is not present
_, err = configMaps.Create(configMap) //so we create a new one
_, err = configMaps.Create(b.CredentialsMap) //so we create a new one
if err != nil {
return errors.Wrap(err, "creating configmap")
}
} else {
_, err := configMaps.Update(configMap) //otherwise update the existing configmap
_, err := configMaps.Update(b.CredentialsMap) //otherwise update the existing configmap
if err != nil {
return errors.Wrap(err, "updating configmap")
}
Expand Down
1 change: 1 addition & 0 deletions pkg/kubernetes/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func (c Copy) CopyFileIntoPod(client *kubernetes.Clientset) error {
if err != nil {
return errors.Wrap(err, "opening tar file")
}
defer f.Close()

tarCmd := []string{"tar", "-zxf", "-", "-C", c.DestPath}

Expand Down

0 comments on commit b0df4e6

Please sign in to comment.