Skip to content

Commit

Permalink
Merge pull request kedgeproject#452 from surajssd/start-s2i-build
Browse files Browse the repository at this point in the history
(feat): s2i build using local code
  • Loading branch information
kadel authored Dec 11, 2017
2 parents 0f5093e + 65ad58e commit a91be10
Show file tree
Hide file tree
Showing 7 changed files with 352 additions and 13 deletions.
30 changes: 24 additions & 6 deletions cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ import (

"github.com/kedgeproject/kedge/pkg/build"

log "github.com/Sirupsen/logrus"
"github.com/spf13/cobra"
)

var Dockerfile string
var DockerImage string
var DockerImage, BuilderImage string
var DockerContext string
var PushImage bool
var PushImage, s2iBuild bool

var buildCmd = &cobra.Command{
Use: "build",
Expand All @@ -38,9 +39,24 @@ var buildCmd = &cobra.Command{
fmt.Println("Please specify the container image name using flag '--image' or '-i'")
os.Exit(-1)
}
if err := build.BuildPushDockerImage(Dockerfile, DockerImage, DockerContext, PushImage); err != nil {
fmt.Println(err)
os.Exit(-1)

if s2iBuild {
if PushImage {
log.Warnf("Using source to image strategy for build, image will be by default pushed to internal container registry, so ignoring this flag")
}
if BuilderImage == "" {
fmt.Println("Please specify the builder image name using flag '--builder-image' or '-b'")
os.Exit(-1)
}
if err := build.BuildS2I(DockerImage, DockerContext, BuilderImage); err != nil {
fmt.Println(err)
os.Exit(-1)
}
} else {
if err := build.BuildPushDockerImage(Dockerfile, DockerImage, DockerContext, PushImage); err != nil {
fmt.Println(err)
os.Exit(-1)
}
}
},
}
Expand All @@ -50,7 +66,9 @@ func init() {
buildCmd.Flags().StringVarP(&Dockerfile, "file", "f", "Dockerfile", "Specify Dockerfile for doing builds, Dockerfile path is relative to context")
buildCmd.Flags().StringVarP(&DockerImage, "image", "i", "", "Image name and tag of resulting image")
buildCmd.Flags().StringVarP(&DockerContext, "context", "c", ".", "Path to a directory containing a Dockerfile, it is build context that is sent to the Docker daemon")
buildCmd.Flags().BoolVarP(&PushImage, "push", "p", false, "Add this flag if you want to push the image")
buildCmd.Flags().BoolVarP(&PushImage, "push", "p", false, "Add this flag if you want to push the image. Note: Ignored when s2i build strategy used")
buildCmd.Flags().BoolVarP(&s2iBuild, "s2i", "", false, "If this is enabled then Source to Image build strategy is used")
buildCmd.Flags().StringVarP(&BuilderImage, "builder-image", "b", "", "Name of a Docker image to use as a builder. Note: This is only useful when using s2i build strategy")

RootCmd.AddCommand(buildCmd)
}
19 changes: 19 additions & 0 deletions docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,22 @@ command before running this build command:
```console
$ eval $(minikube docker-env)
```

### Build using source to image

You can hand off the image building work to OpenShift's source to image utility, without having
to write any Dockerfile.

To do build using OpensShift's s2i run following command:

```console
$ kedge build --s2i --image pyappth -b centos/python-35-centos7:3.5
```

Deconstructing above command, we can see that to enable using OpenShift's s2i use the boolean
flag `--s2i` then give the output name of the ImageStream that will be created using flag
`--image` and finally also provide what builder image to use to do builds of the code using
flag `-b`.

**Note**: Flag `-b` is valid only when using s2i mode. And build flag `-p` is not valid when
in s2i mode.
154 changes: 154 additions & 0 deletions pkg/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,16 @@ import (

log "github.com/Sirupsen/logrus"
dockerlib "github.com/fsouza/go-dockerclient"
"github.com/ghodss/yaml"
os_build_v1 "github.com/openshift/origin/pkg/build/apis/build/v1"
os_image_v1 "github.com/openshift/origin/pkg/image/apis/image/v1"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
_ "k8s.io/kubernetes/pkg/api/install"
kapi "k8s.io/kubernetes/pkg/api/v1"

"github.com/kedgeproject/kedge/pkg/cmd"
"github.com/kedgeproject/kedge/pkg/spec"
)

func BuildPushDockerImage(dockerfile, image, context string, push bool) error {
Expand Down Expand Up @@ -174,3 +183,148 @@ func CreateTarball(source, target string) error {
return err
})
}

// getImageTag get tag name from image name
// if no tag is specified return 'latest'
func GetImageTag(image string) string {
// format: registry_host:registry_port/repo_name/image_name:image_tag
// example:
// 1) myregistryhost:5000/fedora/httpd:version1.0
// 2) myregistryhost:5000/fedora/httpd
// 3) myregistryhost/fedora/httpd:version1.0
// 4) myregistryhost/fedora/httpd
// 5) fedora/httpd
// 6) httpd
imageAndTag := image

imageTagSplit := strings.Split(image, "/")
if len(imageTagSplit) >= 2 {
imageAndTag = imageTagSplit[len(imageTagSplit)-1]
}

p := strings.Split(imageAndTag, ":")
if len(p) == 2 {
return p[1]
}
return "latest"
}

func GetImageName(image string) string {
imageAndTag := image

imageTagSplit := strings.Split(image, "/")
if len(imageTagSplit) >= 2 {
imageAndTag = imageTagSplit[len(imageTagSplit)-1]
}
p := strings.Split(imageAndTag, ":")
if len(p) <= 2 {
return p[0]
}

return image
}

func BuildS2I(image, context, builderImage string) error {

name := GetImageName(image)
labels := map[string]string{
spec.BuildLabelKey: name,
}
annotations := map[string]string{
"openshift.io/generated-by": "KedgeBuildS2I",
}

is := os_image_v1.ImageStream{
TypeMeta: metav1.TypeMeta{
Kind: "ImageStream",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: labels,
Annotations: annotations,
},
Spec: os_image_v1.ImageStreamSpec{
Tags: []os_image_v1.TagReference{
{Name: GetImageTag(image)},
},
},
}

bc := os_build_v1.BuildConfig{
TypeMeta: metav1.TypeMeta{
Kind: "BuildConfig",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Labels: labels,
Annotations: annotations,
},
Spec: os_build_v1.BuildConfigSpec{
CommonSpec: os_build_v1.CommonSpec{
Strategy: os_build_v1.BuildStrategy{
Type: "Binary",
SourceStrategy: &os_build_v1.SourceBuildStrategy{
From: kapi.ObjectReference{
Kind: "DockerImage",
Name: builderImage,
},
},
},
Output: os_build_v1.BuildOutput{
To: &kapi.ObjectReference{
Kind: "ImageStreamTag",
Name: name + ":" + GetImageTag(image),
},
},
},
},
}

isyaml, err := yaml.Marshal(is)
if err != nil {
return err
}

bcyaml, err := yaml.Marshal(bc)
if err != nil {
return err
}

log.Debugf("ImageStream for output image: \n%s\n", string(isyaml))
log.Debugf("BuildConfig: \n%s\n", string(bcyaml))

args := []string{"apply", "-f", "-"}
err = cmd.RunClusterCommand(args, isyaml, true)
if err != nil {
return err
}
err = cmd.RunClusterCommand(args, bcyaml, true)
if err != nil {
cleanup(name)
return err
}

log.Infof("Starting build for %q", image)
cmd := []string{"oc", "start-build", image, "--from-dir=" + context, "-F"}
if err := RunCommand(cmd); err != nil {
return err
}

return nil
}

func cleanup(name string) {
log.Infof("Cleaning up build since error occurred while building")

delBc := []string{"oc", "delete", "buildconfig", name}
if err := RunCommand(delBc); err != nil {
log.Debugf("error while deleting buildconfig: %v", err)
}

delIs := []string{"oc", "delete", "imagestream", name}
if err := RunCommand(delIs); err != nil {
log.Debugf("error while deleting imagestream: %v", err)
}
}
136 changes: 136 additions & 0 deletions pkg/build/build_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
Copyright 2017 The Kedge Authors All rights reserved.
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 build

import (
"testing"

_ "k8s.io/kubernetes/pkg/api/install"
)

func TestGetImageTag(t *testing.T) {

tests := []struct {
name string
input string
want string
}{
{
name: "image FQDN given with port of registry and image tag",
input: "myregistryhost:5000/fedora/httpd:version1.0",
want: "version1.0",
},
{
name: "image FQDN given with port of registry and no image tag",
input: "myregistryhost:5000/fedora/httpd",
want: "latest",
},
{
name: "image FQDN given with image tag",
input: "myregistryhost/fedora/httpd:version1.0",
want: "version1.0",
},
{
name: "image FQDN given without image tag",
input: "myregistryhost/fedora/httpd",
want: "latest",
},
{
name: "repo name/image name without image tag",
input: "fedora/httpd",
want: "latest",
},
{
name: "image name without image tag",
input: "httpd",
want: "latest",
},
{
name: "repo name/image name with image tag",
input: "fedora/httpd:v1",
want: "v1",
},
{
name: "image name with image tag",
input: "httpd:v1",
want: "v1",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := GetImageTag(tt.input); got != tt.want {
t.Errorf("GetImageTag() = %v, want %v", got, tt.want)
}
})
}
}

func TestGetImageName(t *testing.T) {
tests := []struct {
name string
input string
want string
}{
{
name: "image name with image tag",
input: "httpd:v1",
want: "httpd",
},
{
name: "repo name/image name with image tag",
input: "fedora/httpd:v1",
want: "httpd",
},
{
name: "image name without image tag",
input: "httpd",
want: "httpd",
},
{
name: "repo name/image name without image tag",
input: "fedora/httpd",
want: "httpd",
},
{
name: "image FQDN given without image tag",
input: "myregistryhost/fedora/httpd",
want: "httpd",
},
{
name: "image FQDN given with image tag",
input: "myregistryhost/fedora/httpd:version1.0",
want: "httpd",
},
{
name: "image FQDN given with port of registry and no image tag",
input: "myregistryhost:5000/fedora/httpd",
want: "httpd",
},
{
name: "image FQDN given with port of registry and image tag",
input: "myregistryhost:5000/fedora/httpd:version1.0",
want: "httpd",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := GetImageName(tt.input); got != tt.want {
t.Errorf("GetImageName() = %v, want %v", got, tt.want)
}
})
}
}
Loading

0 comments on commit a91be10

Please sign in to comment.