Browse code

Merge pull request #116 from soltysh/card338_2

Merged by openshift-bot

OpenShift Bot authored on 2014/09/26 05:49:18
Showing 19 changed files
... ...
@@ -14,7 +14,7 @@ Getting Started
14 14
 ---------------
15 15
 You can develop [locally on your host](CONTRIBUTING.adoc#develop-locally-on-your-host) or with a [virtual machine](CONTRIBUTING.adoc#develop-on-virtual-machine-using-vagrant), or if you want to just try out OpenShift [download the latest Linux 64bit pre-built binary](CONTRIBUTING.adoc#download-from-github).
16 16
 
17
-First, **get up and running with the** [**Contributing Guide**](CONTRIBUTING.adoc). 
17
+First, **get up and running with the** [**Contributing Guide**](CONTRIBUTING.adoc).
18 18
 
19 19
 Once setup, you can:
20 20
 
... ...
@@ -49,6 +49,26 @@ This example is simply running the ['openshift/hello-openshift' Docker image](ht
49 49
 
50 50
 Remember, you can pass a URL to `-c` when using the `kube` command, so you can [download the latest release](CONTRIBUTING.adoc#download-from-github) and pass a URL to the content on GitHub so you don't even need clone the source.
51 51
 
52
+### Docker registry
53
+
54
+OpenShift builds allow pushing built images into docker registry, for details see [our API](#API).
55
+You can use either private [docker registry](https://github.com/docker/docker-registry) or the
56
+[official docker hub](https://hub.docker.com/). If the two are available private will be favored.
57
+
58
+**Private docker registry**
59
+
60
+To setup private docker registry you can either follow the steps [here](https://github.com/docker/docker-registry#quick-start)
61
+or use [simple-ruby-app example](https://github.com/openshift/origin/blob/master/examples/simple-ruby-app)
62
+to host one inside OpenShift. Now all you need to do is to specify your repository in `buildConfig`.
63
+
64
+**Docker Hub**
65
+
66
+To access the [official docker hub](https://hub.docker.com/) you need to login using `docker login` command.
67
+In result a file named `.dockercfg` is created in your home directory. It contains credentials used
68
+when accessing the hub. Now when running OpenShift, the binary will pick up these credentials and
69
+use them inside build pods to push your result images to the hub.
70
+
71
+NOTE: Make sure to tag your build appropriately to match hub requirements, meaning `username/imagename`.
52 72
 
53 73
 Design Documents
54 74
 ----------------
... ...
@@ -4,6 +4,7 @@
4 4
     "desiredInput": {
5 5
         "builderImage": "openshift/ruby-19-centos",
6 6
         "imageTag": "test-ruby-app",
7
+        "registry": "localhost:5000",
7 8
         "sourceURI": "git://github.com/pmorie/simple-ruby",
8 9
         "type": "sti"
9 10
     },
... ...
@@ -1,4 +1,4 @@
1
-<!DOCTYPE HTML><html><head><title>OpenShift 3 API documentation</title><meta http-equiv=X-UA-Compatible content="IE=edge"><meta http-equiv=Content-Type content="text/html; charset=utf-8"><meta name=generator content="https://github.com/kevinrenskers/raml2html 1.0.3"><link rel=stylesheet href=http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css><link rel=stylesheet href=http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.1/styles/default.min.css><script type=text/javascript src=http://code.jquery.com/jquery-1.11.0.min.js></script><script type=text/javascript src=http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js></script><script type=text/javascript src=http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.1/highlight.min.js></script><script type=text/javascript>
1
+<!DOCTYPE HTML><html><head><title>OpenShift 3 API documentation</title><meta http-equiv=X-UA-Compatible content="IE=edge"><meta http-equiv=Content-Type content="text/html; charset=utf-8"><meta name=generator content="https://github.com/kevinrenskers/raml2html 1.0.4"><link rel=stylesheet href=http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css><link rel=stylesheet href=http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.1/styles/default.min.css><script type=text/javascript src=http://code.jquery.com/jquery-1.11.0.min.js></script><script type=text/javascript src=http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js></script><script type=text/javascript src=http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.1/highlight.min.js></script><script type=text/javascript>
2 2
         $(document).ready(function() {
3 3
             $('.page-header pre code, .top-resource-description pre code').each(function(i, block) {
4 4
                 hljs.highlightBlock(block);
... ...
@@ -191,6 +191,7 @@
191 191
     "desiredInput": {
192 192
         "builderImage": "openshift/ruby-19-centos",
193 193
         "imageTag": "test-ruby-app",
194
+        "registry": "localhost:5000",
194 195
         "sourceURI": "git://github.com/pmorie/simple-ruby",
195 196
         "type": "sti"
196 197
     },
... ...
@@ -204,6 +205,7 @@
204 204
     "desiredInput": {
205 205
         "builderImage": "openshift/ruby-19-centos",
206 206
         "imageTag": "test-ruby-app",
207
+        "registry": "localhost:5000",
207 208
         "sourceURI": "git://github.com/pmorie/simple-ruby",
208 209
         "type": "sti"
209 210
     },
... ...
@@ -2,10 +2,11 @@
2 2
     "id": "build100",
3 3
     "desiredInput":
4 4
     {
5
-        "type": "docker",
5
+        "type":      "docker",
6 6
         "sourceURI": "git://github.com/openshift/ruby-hello-world.git",
7
-        "imageTag":     "openshift/origin-ruby-sample",
7
+        "imageTag":  "openshift/origin-ruby-sample",
8
+        "registry":  "localhost:5000",
8 9
     },
9
- 
10
+
10 11
     "secret": "secret101"
11 12
 }
12 13
deleted file mode 100644
... ...
@@ -1,27 +0,0 @@
1
-{
2
-   "zen":"Encourage flow.",
3
-   "hook":{
4
-      "url":"https://api.github.com/repos/anonUser/anonRepo/hooks/2896466",
5
-      "test_url":"https://api.github.com/repos/anonUser/anonRepo/hooks/2896466/test",
6
-      "id":2896466,
7
-      "name":"web",
8
-      "active":true,
9
-      "events":[
10
-         "push"
11
-      ],
12
-      "config":{
13
-         "secret":"",
14
-         "url":"http://example.com/gitwebhook",
15
-         "content_type":"json",
16
-         "insecure_ssl":"0"
17
-      },
18
-      "last_response":{
19
-         "code":null,
20
-         "status":"unused",
21
-         "message":null
22
-      },
23
-      "updated_at":"2014-08-28T14:25:47Z",
24
-      "created_at":"2014-08-28T14:25:47Z"
25
-   },
26
-   "hook_id":2896466
27
-}
... ...
@@ -4,6 +4,8 @@ echo "Killing openshift all-in-one server"
4 4
 killall openshift
5 5
 echo "Cleaning up openshift etcd content"
6 6
 rm -rf openshift.local.etcd
7
+echo "Cleaning up openshift etcd volumes"
8
+rm -rf openshift.local.volumes
7 9
 echo "Killing all docker containers on host"
8 10
 docker kill `docker ps --no-trunc -q`
9 11
 
... ...
@@ -15,11 +15,9 @@ rm -rf openshift.local.etcd
15 15
 
16 16
 
17 17
 # Start the OpenShift all-in-one server
18
-# (starts a kubernetes master and minion as well as providing the
19
-# origin REST api)
20
-# DOCKER_REGISTRY refers to the private Docker registry running inside a pod in OpenShift.
18
+# (starts a kubernetes master and minion as well as providing the origin REST api)
21 19
 echo "Launching openshift all-in-one server"
22
-DOCKER_REGISTRY=localhost:5000 $openshift start --listenAddr="0.0.0.0:8080" &> logs/openshift.log &
20
+$openshift start --listenAddr="0.0.0.0:8080" &> logs/openshift.log &
23 21
 
24 22
 sleep 5
25 23
 
... ...
@@ -16,6 +16,6 @@ fi
16 16
 
17 17
 docker build --rm -t $TAG $DOCKER_CONTEXT_URL
18 18
 
19
-if [ -n "$DOCKER_REGISTRY" ]; then
19
+if [ -n "$DOCKER_REGISTRY" ] || [ -s "/.dockercfg" ]; then
20 20
   docker push $TAG
21 21
 fi
... ...
@@ -17,9 +17,9 @@ if [ -n "$SOURCE_REF" ]; then
17 17
   REF_OPTION="--ref $SOURCE_REF"
18 18
 fi
19 19
 
20
-BUILD_TEMP_DIR=${TEMP_DIR-$TMPDIR} 
20
+BUILD_TEMP_DIR=${TEMP_DIR-$TMPDIR}
21 21
 TMPDIR=$BUILD_TEMP_DIR sti build $SOURCE_URI $BUILDER_IMAGE $TAG $REF_OPTION
22 22
 
23
-if [ -n "$DOCKER_REGISTRY" ]; then
23
+if [ -n "$DOCKER_REGISTRY" ] || [ -s "/.dockercfg" ]; then
24 24
   docker push $TAG
25 25
 fi
... ...
@@ -35,6 +35,9 @@ type BuildInput struct {
35 35
 	// ImageTag is the tag to give to the image resulting from the build
36 36
 	ImageTag string `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
37 37
 
38
+	// Registry to push the result image to
39
+	Registry string `json:"registry",omitempty" yaml:"registry,omitempty"`
40
+
38 41
 	// BuilderImage is the image used to execute the build when running STI builds
39 42
 	BuilderImage string `json:"builderImage,omitempty" yaml:"builderImage,omitempty"`
40 43
 }
... ...
@@ -35,6 +35,9 @@ type BuildInput struct {
35 35
 	// ImageTag is the tag to give to the image resulting from the build
36 36
 	ImageTag string `json:"imageTag,omitempty" yaml:"imageTag,omitempty"`
37 37
 
38
+	// Registry to push the result image to
39
+	Registry string `json:"registry",omitempty" yaml:"registry,omitempty"`
40
+
38 41
 	// BuilderImage is the image used to execute the build when running STI builds
39 42
 	BuilderImage string `json:"builderImage,omitempty" yaml:"builderImage,omitempty"`
40 43
 }
... ...
@@ -18,7 +18,7 @@ import (
18 18
 // BuildJobStrategy represents a strategy for executing a build by
19 19
 // creating a pod definition that will execute the build
20 20
 type BuildJobStrategy interface {
21
-	CreateBuildPod(build *api.Build, dockerImage string) (*kubeapi.Pod, error)
21
+	CreateBuildPod(build *api.Build) (*kubeapi.Pod, error)
22 22
 }
23 23
 
24 24
 // BuildController watches build resources and manages their state
... ...
@@ -26,7 +26,6 @@ type BuildController struct {
26 26
 	osClient        osclient.Interface
27 27
 	kubeClient      kubeclient.Interface
28 28
 	buildStrategies map[api.BuildType]BuildJobStrategy
29
-	dockerRegistry  string
30 29
 	timeout         int
31 30
 }
32 31
 
... ...
@@ -34,17 +33,14 @@ type BuildController struct {
34 34
 func NewBuildController(kc kubeclient.Interface,
35 35
 	oc osclient.Interface,
36 36
 	strategies map[api.BuildType]BuildJobStrategy,
37
-	registry string,
38 37
 	timeout int) *BuildController {
39 38
 
40
-	glog.Infof("Creating build controller with dockerRegistry=%s, timeout=%d",
41
-		registry, timeout)
39
+	glog.Infof("Creating build controller with timeout=%d", timeout)
42 40
 
43 41
 	bc := &BuildController{
44 42
 		kubeClient:      kc,
45 43
 		osClient:        oc,
46 44
 		buildStrategies: strategies,
47
-		dockerRegistry:  registry,
48 45
 		timeout:         timeout,
49 46
 	}
50 47
 	return bc
... ...
@@ -107,7 +103,7 @@ func (bc *BuildController) synchronize(build *api.Build) (api.BuildStatus, error
107 107
 			return api.BuildError, fmt.Errorf("No build type for %s", build.Input.Type)
108 108
 		}
109 109
 
110
-		podSpec, err := buildStrategy.CreateBuildPod(build, bc.dockerRegistry)
110
+		podSpec, err := buildStrategy.CreateBuildPod(build)
111 111
 		if err != nil {
112 112
 			glog.Errorf("Unable to create build pod: %v", err)
113 113
 			return api.BuildFailed, err
... ...
@@ -33,7 +33,7 @@ func (_ *errOsClient) UpdateBuild(build *api.Build) (*api.Build, error) {
33 33
 
34 34
 type okStrategy struct{}
35 35
 
36
-func (_ *okStrategy) CreateBuildPod(build *api.Build, dockerRegistry string) (*kubeapi.Pod, error) {
36
+func (_ *okStrategy) CreateBuildPod(build *api.Build) (*kubeapi.Pod, error) {
37 37
 	return &kubeapi.Pod{}, nil
38 38
 }
39 39
 
... ...
@@ -17,7 +17,7 @@ func NewDockerBuildStrategy(dockerBuilderImage string) *DockerBuildStrategy {
17 17
 
18 18
 // CreateBuildPod creates the pod to be used for the Docker build
19 19
 // TODO: Make the Pod definition configurable
20
-func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build, dockerRegistry string) (*api.Pod, error) {
20
+func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build) (*api.Pod, error) {
21 21
 	pod := &api.Pod{
22 22
 		JSONBase: api.JSONBase{
23 23
 			ID: build.PodID,
... ...
@@ -32,7 +32,7 @@ func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build, dockerRegis
32 32
 						Env: []api.EnvVar{
33 33
 							{Name: "BUILD_TAG", Value: build.Input.ImageTag},
34 34
 							{Name: "DOCKER_CONTEXT_URL", Value: build.Input.SourceURI},
35
-							{Name: "DOCKER_REGISTRY", Value: dockerRegistry},
35
+							{Name: "DOCKER_REGISTRY", Value: build.Input.Registry},
36 36
 						},
37 37
 					},
38 38
 				},
... ...
@@ -44,5 +44,6 @@ func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build, dockerRegis
44 44
 	}
45 45
 
46 46
 	setupDockerSocket(pod)
47
+	setupDockerConfig(pod)
47 48
 	return pod, nil
48 49
 }
... ...
@@ -8,10 +8,9 @@ import (
8 8
 )
9 9
 
10 10
 func TestDockerCreateBuildPod(t *testing.T) {
11
-	const dockerRegistry = "docker-test-registry"
12 11
 	strategy := NewDockerBuildStrategy("docker-test-image")
13 12
 	expected := mockDockerBuild()
14
-	actual, _ := strategy.CreateBuildPod(expected, dockerRegistry)
13
+	actual, _ := strategy.CreateBuildPod(expected)
15 14
 
16 15
 	if actual.JSONBase.ID != expected.PodID {
17 16
 		t.Errorf("Expected %s, but got %s!", expected.PodID, actual.JSONBase.ID)
... ...
@@ -36,8 +35,8 @@ func TestDockerCreateBuildPod(t *testing.T) {
36 36
 	if e := container.Env[1]; e.Name != "DOCKER_CONTEXT_URL" && e.Value != expected.Input.SourceURI {
37 37
 		t.Errorf("Expected %s, got %s:%s!", expected.Input.ImageTag, e.Name, e.Value)
38 38
 	}
39
-	if e := container.Env[2]; e.Name != "DOCKER_REGISTRY" && e.Value != dockerRegistry {
40
-		t.Errorf("Expected %s got %s:%s!", dockerRegistry, e.Name, e.Value)
39
+	if e := container.Env[2]; e.Name != "DOCKER_REGISTRY" && e.Value != expected.Input.Registry {
40
+		t.Errorf("Expected %s got %s:%s!", expected.Input.Registry, e.Name, e.Value)
41 41
 	}
42 42
 }
43 43
 
... ...
@@ -50,6 +49,7 @@ func mockDockerBuild() *api.Build {
50 50
 			Type:      api.DockerBuildType,
51 51
 			SourceURI: "http://my.build.com/the/dockerbuild/Dockerfile",
52 52
 			ImageTag:  "repository/dockerBuild",
53
+			Registry:  "docker-registry",
53 54
 		},
54 55
 		Status: api.BuildNew,
55 56
 		PodID:  "-the-pod-id",
... ...
@@ -33,7 +33,7 @@ func NewSTIBuildStrategy(stiBuilderImage string, tc TempDirectoryCreator) *STIBu
33 33
 
34 34
 // CreateBuildPod creates a pod that will execute the STI build
35 35
 // TODO: Make the Pod definition configurable
36
-func (bs *STIBuildStrategy) CreateBuildPod(build *buildapi.Build, dockerRegistry string) (*api.Pod, error) {
36
+func (bs *STIBuildStrategy) CreateBuildPod(build *buildapi.Build) (*api.Pod, error) {
37 37
 	pod := &api.Pod{
38 38
 		JSONBase: api.JSONBase{
39 39
 			ID: build.PodID,
... ...
@@ -47,7 +47,7 @@ func (bs *STIBuildStrategy) CreateBuildPod(build *buildapi.Build, dockerRegistry
47 47
 						Image: bs.stiBuilderImage,
48 48
 						Env: []api.EnvVar{
49 49
 							{Name: "BUILD_TAG", Value: build.Input.ImageTag},
50
-							{Name: "DOCKER_REGISTRY", Value: dockerRegistry},
50
+							{Name: "DOCKER_REGISTRY", Value: build.Input.Registry},
51 51
 							{Name: "SOURCE_URI", Value: build.Input.SourceURI},
52 52
 							{Name: "SOURCE_REF", Value: build.Input.SourceRef},
53 53
 							{Name: "BUILDER_IMAGE", Value: build.Input.BuilderImage},
... ...
@@ -66,6 +66,7 @@ func (bs *STIBuildStrategy) CreateBuildPod(build *buildapi.Build, dockerRegistry
66 66
 	}
67 67
 
68 68
 	setupDockerSocket(pod)
69
+	setupDockerConfig(pod)
69 70
 	return pod, nil
70 71
 }
71 72
 
... ...
@@ -14,10 +14,9 @@ func (t *FakeTempDirCreator) CreateTempDirectory() (string, error) {
14 14
 }
15 15
 
16 16
 func TestSTICreateBuildPod(t *testing.T) {
17
-	const dockerRegistry = "sti-test-registry"
18 17
 	strategy := NewSTIBuildStrategy("sti-test-image", &FakeTempDirCreator{})
19 18
 	expected := mockSTIBuild()
20
-	actual, _ := strategy.CreateBuildPod(expected, dockerRegistry)
19
+	actual, _ := strategy.CreateBuildPod(expected)
21 20
 
22 21
 	if actual.JSONBase.ID != expected.PodID {
23 22
 		t.Errorf("Expected %s, but got %s!", expected.PodID, actual.JSONBase.ID)
... ...
@@ -39,8 +38,8 @@ func TestSTICreateBuildPod(t *testing.T) {
39 39
 	if e := container.Env[0]; e.Name != "BUILD_TAG" || e.Value != expected.Input.ImageTag {
40 40
 		t.Errorf("Expected %s, got %s:%s!", expected.Input.ImageTag, e.Name, e.Value)
41 41
 	}
42
-	if e := container.Env[1]; e.Name != "DOCKER_REGISTRY" || e.Value != dockerRegistry {
43
-		t.Errorf("Expected %s got %s:%s!", dockerRegistry, e.Name, e.Value)
42
+	if e := container.Env[1]; e.Name != "DOCKER_REGISTRY" || e.Value != expected.Input.Registry {
43
+		t.Errorf("Expected %s got %s:%s!", expected.Input.Registry, e.Name, e.Value)
44 44
 	}
45 45
 	if e := container.Env[2]; e.Name != "SOURCE_URI" || e.Value != expected.Input.SourceURI {
46 46
 		t.Errorf("Expected %s got %s:%s!", expected.Input.SourceURI, e.Name, e.Value)
... ...
@@ -62,6 +61,7 @@ func mockSTIBuild() *api.Build {
62 62
 			Type:      api.STIBuildType,
63 63
 			SourceURI: "http://my.build.com/the/stibuild/Dockerfile",
64 64
 			ImageTag:  "repository/stiBuild",
65
+			Registry:  "docker-registry",
65 66
 		},
66 67
 		Status: api.BuildNew,
67 68
 		PodID:  "-the-pod-id",
... ...
@@ -1,6 +1,9 @@
1 1
 package strategy
2 2
 
3 3
 import (
4
+	"os"
5
+	"path"
6
+
4 7
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
5 8
 )
6 9
 
... ...
@@ -26,3 +29,31 @@ func setupDockerSocket(podSpec *api.Pod) {
26 26
 		append(podSpec.DesiredState.Manifest.Containers[0].VolumeMounts,
27 27
 			dockerSocketVolumeMount)
28 28
 }
29
+
30
+// setupDockerConfig configures the path to .dockercfg which contains registry credentials
31
+func setupDockerConfig(podSpec *api.Pod) {
32
+	dockerConfig := path.Join(os.Getenv("HOME"), ".dockercfg")
33
+	if _, err := os.Stat(dockerConfig); os.IsNotExist(err) {
34
+		return
35
+	}
36
+	dockerConfigVolume := api.Volume{
37
+		Name: "docker-cfg",
38
+		Source: &api.VolumeSource{
39
+			HostDirectory: &api.HostDirectory{
40
+				Path: dockerConfig,
41
+			},
42
+		},
43
+	}
44
+
45
+	dockerConfigVolumeMount := api.VolumeMount{
46
+		Name:      "docker-cfg",
47
+		ReadOnly:  true,
48
+		MountPath: "/.dockercfg",
49
+	}
50
+
51
+	podSpec.DesiredState.Manifest.Volumes = append(podSpec.DesiredState.Manifest.Volumes,
52
+		dockerConfigVolume)
53
+	podSpec.DesiredState.Manifest.Containers[0].VolumeMounts =
54
+		append(podSpec.DesiredState.Manifest.Containers[0].VolumeMounts,
55
+			dockerConfigVolumeMount)
56
+}
... ...
@@ -408,14 +408,13 @@ func (c *config) runBuildController() {
408 408
 	// initialize build controller
409 409
 	dockerBuilderImage := env("OPENSHIFT_DOCKER_BUILDER_IMAGE", "openshift/docker-builder")
410 410
 	stiBuilderImage := env("OPENSHIFT_STI_BUILDER_IMAGE", "openshift/sti-builder")
411
-	dockerRegistry := env("DOCKER_REGISTRY", "")
412 411
 
413 412
 	buildStrategies := map[buildapi.BuildType]build.BuildJobStrategy{
414 413
 		buildapi.DockerBuildType: strategy.NewDockerBuildStrategy(dockerBuilderImage),
415 414
 		buildapi.STIBuildType:    strategy.NewSTIBuildStrategy(stiBuilderImage, strategy.STITempDirectoryCreator),
416 415
 	}
417 416
 
418
-	buildController := build.NewBuildController(kubeClient, osClient, buildStrategies, dockerRegistry, 1200)
417
+	buildController := build.NewBuildController(kubeClient, osClient, buildStrategies, 1200)
419 418
 	buildController.Run(10 * time.Second)
420 419
 }
421 420