| ... | ... |
@@ -14501,6 +14501,10 @@ |
| 14501 | 14501 |
"$ref": "time.Duration", |
| 14502 | 14502 |
"description": "amount of time the build has been running" |
| 14503 | 14503 |
}, |
| 14504 |
+ "outputDockerImageReference": {
|
|
| 14505 |
+ "type": "string", |
|
| 14506 |
+ "description": "reference to the Docker image built by this build, computed from build.spec.output.to, and can be used to push and pull the image" |
|
| 14507 |
+ }, |
|
| 14504 | 14508 |
"config": {
|
| 14505 | 14509 |
"$ref": "v1.ObjectReference", |
| 14506 | 14510 |
"description": "reference to build config from which this build was derived" |
| ... | ... |
@@ -935,6 +935,7 @@ func deepCopy_api_BuildStatus(in buildapi.BuildStatus, out *buildapi.BuildStatus |
| 935 | 935 |
out.CompletionTimestamp = nil |
| 936 | 936 |
} |
| 937 | 937 |
out.Duration = in.Duration |
| 938 |
+ out.OutputDockerImageReference = in.OutputDockerImageReference |
|
| 938 | 939 |
if in.Config != nil {
|
| 939 | 940 |
if newVal, err := c.DeepCopy(in.Config); err != nil {
|
| 940 | 941 |
return err |
| ... | ... |
@@ -357,9 +357,6 @@ func roundTrip(t *testing.T, codec runtime.Codec, originalItem runtime.Object) {
|
| 357 | 357 |
var skipStandardVersions = map[string][]string{
|
| 358 | 358 |
"DockerImage": {"pre012", "1.0"},
|
| 359 | 359 |
} |
| 360 |
-var skipV1beta1 = map[string]struct{}{}
|
|
| 361 |
-var skipV1beta3 = map[string]struct{}{}
|
|
| 362 |
-var skipV1 = map[string]struct{}{}
|
|
| 363 | 360 |
|
| 364 | 361 |
const fuzzIters = 20 |
| 365 | 362 |
|
| ... | ... |
@@ -408,14 +405,10 @@ func TestTypes(t *testing.T) {
|
| 408 | 408 |
} |
| 409 | 409 |
fuzzInternalObject(t, "", item, seed) |
| 410 | 410 |
roundTrip(t, osapi.Codec, item) |
| 411 |
- if _, ok := skipV1beta3[kind]; !ok {
|
|
| 412 |
- fuzzInternalObject(t, "v1beta3", item, seed) |
|
| 413 |
- roundTrip(t, v1beta3.Codec, item) |
|
| 414 |
- } |
|
| 415 |
- if _, ok := skipV1[kind]; !ok {
|
|
| 416 |
- fuzzInternalObject(t, "v1", item, seed) |
|
| 417 |
- roundTrip(t, v1.Codec, item) |
|
| 418 |
- } |
|
| 411 |
+ fuzzInternalObject(t, "v1beta3", item, seed) |
|
| 412 |
+ roundTrip(t, v1beta3.Codec, item) |
|
| 413 |
+ fuzzInternalObject(t, "v1", item, seed) |
|
| 414 |
+ roundTrip(t, v1.Codec, item) |
|
| 419 | 415 |
} |
| 420 | 416 |
} |
| 421 | 417 |
} |
| ... | ... |
@@ -796,6 +796,7 @@ func convert_api_BuildStatus_To_v1_BuildStatus(in *buildapi.BuildStatus, out *ap |
| 796 | 796 |
out.CompletionTimestamp = nil |
| 797 | 797 |
} |
| 798 | 798 |
out.Duration = in.Duration |
| 799 |
+ out.OutputDockerImageReference = in.OutputDockerImageReference |
|
| 799 | 800 |
if in.Config != nil {
|
| 800 | 801 |
out.Config = new(pkgapiv1.ObjectReference) |
| 801 | 802 |
if err := convert_api_ObjectReference_To_v1_ObjectReference(in.Config, out.Config, s); err != nil {
|
| ... | ... |
@@ -1183,6 +1184,7 @@ func convert_v1_BuildStatus_To_api_BuildStatus(in *apiv1.BuildStatus, out *build |
| 1183 | 1183 |
out.CompletionTimestamp = nil |
| 1184 | 1184 |
} |
| 1185 | 1185 |
out.Duration = in.Duration |
| 1186 |
+ out.OutputDockerImageReference = in.OutputDockerImageReference |
|
| 1186 | 1187 |
if in.Config != nil {
|
| 1187 | 1188 |
out.Config = new(pkgapi.ObjectReference) |
| 1188 | 1189 |
if err := convert_v1_ObjectReference_To_api_ObjectReference(in.Config, out.Config, s); err != nil {
|
| ... | ... |
@@ -960,6 +960,7 @@ func deepCopy_v1_BuildStatus(in apiv1.BuildStatus, out *apiv1.BuildStatus, c *co |
| 960 | 960 |
out.CompletionTimestamp = nil |
| 961 | 961 |
} |
| 962 | 962 |
out.Duration = in.Duration |
| 963 |
+ out.OutputDockerImageReference = in.OutputDockerImageReference |
|
| 963 | 964 |
if in.Config != nil {
|
| 964 | 965 |
if newVal, err := c.DeepCopy(in.Config); err != nil {
|
| 965 | 966 |
return err |
| ... | ... |
@@ -834,6 +834,7 @@ func convert_api_BuildStatus_To_v1beta3_BuildStatus(in *buildapi.BuildStatus, ou |
| 834 | 834 |
out.CompletionTimestamp = nil |
| 835 | 835 |
} |
| 836 | 836 |
out.Duration = in.Duration |
| 837 |
+ out.OutputDockerImageReference = in.OutputDockerImageReference |
|
| 837 | 838 |
if in.Config != nil {
|
| 838 | 839 |
out.Config = new(pkgapiv1beta3.ObjectReference) |
| 839 | 840 |
if err := convert_api_ObjectReference_To_v1beta3_ObjectReference(in.Config, out.Config, s); err != nil {
|
| ... | ... |
@@ -1221,6 +1222,7 @@ func convert_v1beta3_BuildStatus_To_api_BuildStatus(in *apiv1beta3.BuildStatus, |
| 1221 | 1221 |
out.CompletionTimestamp = nil |
| 1222 | 1222 |
} |
| 1223 | 1223 |
out.Duration = in.Duration |
| 1224 |
+ out.OutputDockerImageReference = in.OutputDockerImageReference |
|
| 1224 | 1225 |
if in.Config != nil {
|
| 1225 | 1226 |
out.Config = new(pkgapi.ObjectReference) |
| 1226 | 1227 |
if err := convert_v1beta3_ObjectReference_To_api_ObjectReference(in.Config, out.Config, s); err != nil {
|
| ... | ... |
@@ -968,6 +968,7 @@ func deepCopy_v1beta3_BuildStatus(in apiv1beta3.BuildStatus, out *apiv1beta3.Bui |
| 968 | 968 |
out.CompletionTimestamp = nil |
| 969 | 969 |
} |
| 970 | 970 |
out.Duration = in.Duration |
| 971 |
+ out.OutputDockerImageReference = in.OutputDockerImageReference |
|
| 971 | 972 |
if in.Config != nil {
|
| 972 | 973 |
if newVal, err := c.DeepCopy(in.Config); err != nil {
|
| 973 | 974 |
return err |
| ... | ... |
@@ -84,6 +84,12 @@ type BuildStatus struct {
|
| 84 | 84 |
// Duration contains time.Duration object describing build time. |
| 85 | 85 |
Duration time.Duration |
| 86 | 86 |
|
| 87 |
+ // OutputDockerImageReference contains a reference to the Docker image that |
|
| 88 |
+ // will be built by this build. It's value is computed from |
|
| 89 |
+ // Build.Spec.Output.To, and should include the registry address, so that |
|
| 90 |
+ // it can be used to push and pull the image. |
|
| 91 |
+ OutputDockerImageReference string |
|
| 92 |
+ |
|
| 87 | 93 |
// Config is an ObjectReference to the BuildConfig this Build is based on. |
| 88 | 94 |
Config *kapi.ObjectReference |
| 89 | 95 |
} |
| ... | ... |
@@ -69,6 +69,12 @@ type BuildStatus struct {
|
| 69 | 69 |
// Duration contains time.Duration object describing build time. |
| 70 | 70 |
Duration time.Duration `json:"duration,omitempty" description:"amount of time the build has been running"` |
| 71 | 71 |
|
| 72 |
+ // OutputDockerImageReference contains a reference to the Docker image that |
|
| 73 |
+ // will be built by this build. It's value is computed from |
|
| 74 |
+ // Build.Spec.Output.To, and should include the registry address, so that |
|
| 75 |
+ // it can be used to push and pull the image. |
|
| 76 |
+ OutputDockerImageReference string `json:"outputDockerImageReference,omitempty" description:"reference to the Docker image built by this build, computed from build.spec.output.to, and can be used to push and pull the image"` |
|
| 77 |
+ |
|
| 72 | 78 |
// Config is an ObjectReference to the BuildConfig this Build is based on. |
| 73 | 79 |
Config *kapi.ObjectReference `json:"config,omitempty" description:"reference to build config from which this build was derived"` |
| 74 | 80 |
} |
| ... | ... |
@@ -69,6 +69,12 @@ type BuildStatus struct {
|
| 69 | 69 |
// Duration contains time.Duration object describing build time. |
| 70 | 70 |
Duration time.Duration `json:"duration,omitempty"` |
| 71 | 71 |
|
| 72 |
+ // OutputDockerImageReference contains a reference to the Docker image that |
|
| 73 |
+ // will be built by this build. It's value is computed from |
|
| 74 |
+ // Build.Spec.Output.To, and should include the registry address, so that |
|
| 75 |
+ // it can be used to push and pull the image. |
|
| 76 |
+ OutputDockerImageReference string `json:"outputDockerImageReference,omitempty" description:"reference to the Docker image built by this build, computed from build.spec.output.to, and can be used to push and pull the image"` |
|
| 77 |
+ |
|
| 72 | 78 |
// Config is an ObjectReference to the BuildConfig this Build is based on. |
| 73 | 79 |
Config *kapi.ObjectReference `json:"config,omitempty"` |
| 74 | 80 |
} |
| ... | ... |
@@ -2,9 +2,10 @@ package controller |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 |
- "github.com/golang/glog" |
|
| 6 | 5 |
"strings" |
| 7 | 6 |
|
| 7 |
+ "github.com/golang/glog" |
|
| 8 |
+ |
|
| 8 | 9 |
kapi "k8s.io/kubernetes/pkg/api" |
| 9 | 10 |
errors "k8s.io/kubernetes/pkg/api/errors" |
| 10 | 11 |
"k8s.io/kubernetes/pkg/client/cache" |
| ... | ... |
@@ -115,61 +116,38 @@ func (bc *BuildController) nextBuildPhase(build *buildapi.Build) error {
|
| 115 | 115 |
return nil |
| 116 | 116 |
} |
| 117 | 117 |
|
| 118 |
- // lookup the destination from the referenced image repository |
|
| 119 |
- var spec string |
|
| 120 |
- if ref := build.Spec.Output.To; ref != nil && len(ref.Name) != 0 {
|
|
| 121 |
- switch {
|
|
| 122 |
- case ref.Kind == "DockerImage": |
|
| 123 |
- spec = ref.Name |
|
| 124 |
- case ref.Kind == "ImageStream" || ref.Kind == "ImageStreamTag": |
|
| 125 |
- // TODO: security, ensure that the reference image stream is actually visible |
|
| 126 |
- namespace := ref.Namespace |
|
| 127 |
- if len(namespace) == 0 {
|
|
| 128 |
- namespace = build.Namespace |
|
| 129 |
- } |
|
| 130 |
- |
|
| 131 |
- var tag string |
|
| 132 |
- streamName := ref.Name |
|
| 133 |
- if ref.Kind == "ImageStreamTag" {
|
|
| 134 |
- bits := strings.Split(ref.Name, ":") |
|
| 135 |
- streamName = bits[0] |
|
| 136 |
- tag = ":" + bits[1] |
|
| 137 |
- } |
|
| 138 |
- stream, err := bc.ImageStreamClient.GetImageStream(namespace, streamName) |
|
| 139 |
- if err != nil {
|
|
| 140 |
- if errors.IsNotFound(err) {
|
|
| 141 |
- return fmt.Errorf("the referenced output ImageStream %s/%s does not exist", namespace, streamName)
|
|
| 142 |
- } |
|
| 143 |
- return fmt.Errorf("the referenced output ImageStream %s/%s could not be found by Build %s/%s: %v", namespace, streamName, build.Namespace, build.Name, err)
|
|
| 144 |
- } |
|
| 145 |
- if len(stream.Status.DockerImageRepository) == 0 {
|
|
| 146 |
- e := fmt.Errorf("the ImageStream %s/%s cannot be used as the output for Build %s/%s because the integrated Docker registry is not configured, or the user forgot to set a valid external registry", namespace, ref.Name, build.Namespace, build.Name)
|
|
| 147 |
- bc.Recorder.Eventf(build, "invalidOutput", "Error starting build: %v", e) |
|
| 148 |
- return e |
|
| 149 |
- } |
|
| 150 |
- spec = fmt.Sprintf("%s%s", stream.Status.DockerImageRepository, tag)
|
|
| 151 |
- } |
|
| 118 |
+ // Set the output Docker image reference. |
|
| 119 |
+ ref, err := bc.resolveOutputDockerImageReference(build) |
|
| 120 |
+ if err != nil {
|
|
| 121 |
+ return err |
|
| 152 | 122 |
} |
| 123 |
+ build.Status.OutputDockerImageReference = ref |
|
| 153 | 124 |
|
| 154 |
- // set the expected build parameters, which will be saved if no error occurs |
|
| 125 |
+ // Set the build phase, which will be persisted if no error occurs. |
|
| 155 | 126 |
build.Status.Phase = buildapi.BuildPhasePending |
| 156 | 127 |
|
| 157 |
- // Make a copy to avoid mutating the build from this point on |
|
| 128 |
+ // Make a copy to avoid mutating the build from this point on. |
|
| 158 | 129 |
copy, err := kapi.Scheme.Copy(build) |
| 159 | 130 |
if err != nil {
|
| 160 | 131 |
return fmt.Errorf("unable to copy Build: %v", err)
|
| 161 | 132 |
} |
| 162 | 133 |
buildCopy := copy.(*buildapi.Build) |
| 163 | 134 |
|
| 164 |
- // override the Output to be a DockerImage type in the strategy for the copy we send to the build pod |
|
| 135 |
+ // TODO(rhcarvalho) |
|
| 136 |
+ // The S2I and Docker builders expect build.Spec.Output.To to contain a |
|
| 137 |
+ // resolved reference to a Docker image. Since build.Spec is immutable, we |
|
| 138 |
+ // change a copy (that is never persisted) and pass it to |
|
| 139 |
+ // bc.BuildStrategy.CreateBuildPod. We should make the builders use |
|
| 140 |
+ // build.Status.OutputDockerImageReference, what will make copying the build |
|
| 141 |
+ // unnecessary. |
|
| 165 | 142 |
if build.Spec.Output.To != nil && len(build.Spec.Output.To.Name) != 0 {
|
| 166 | 143 |
buildCopy.Spec.Output.To = &kapi.ObjectReference{
|
| 167 | 144 |
Kind: "DockerImage", |
| 168 |
- Name: spec, |
|
| 145 |
+ Name: ref, |
|
| 169 | 146 |
} |
| 170 | 147 |
} |
| 171 | 148 |
|
| 172 |
- // invoke the strategy to get a build pod |
|
| 149 |
+ // Invoke the strategy to get a build pod. |
|
| 173 | 150 |
podSpec, err := bc.BuildStrategy.CreateBuildPod(buildCopy) |
| 174 | 151 |
if err != nil {
|
| 175 | 152 |
return fmt.Errorf("the strategy failed to create a build pod for Build %s/%s: %v", build.Namespace, build.Name, err)
|
| ... | ... |
@@ -181,7 +159,7 @@ func (bc *BuildController) nextBuildPhase(build *buildapi.Build) error {
|
| 181 | 181 |
glog.V(4).Infof("Build pod already existed: %#v", podSpec)
|
| 182 | 182 |
return nil |
| 183 | 183 |
} |
| 184 |
- // log an event if the pod is not created (most likely due to quota denial) |
|
| 184 |
+ // Log an event if the pod is not created (most likely due to quota denial). |
|
| 185 | 185 |
bc.Recorder.Eventf(build, "failedCreate", "Error creating: %v", err) |
| 186 | 186 |
return fmt.Errorf("failed to create pod for Build %s/%s: %v", build.Namespace, build.Name, err)
|
| 187 | 187 |
} |
| ... | ... |
@@ -190,6 +168,48 @@ func (bc *BuildController) nextBuildPhase(build *buildapi.Build) error {
|
| 190 | 190 |
return nil |
| 191 | 191 |
} |
| 192 | 192 |
|
| 193 |
+// resolveOutputDockerImageReference returns a reference to a Docker image |
|
| 194 |
+// computed from the buid.Spec.Output.To reference. |
|
| 195 |
+func (bc *BuildController) resolveOutputDockerImageReference(build *buildapi.Build) (string, error) {
|
|
| 196 |
+ outputTo := build.Spec.Output.To |
|
| 197 |
+ if outputTo == nil || outputTo.Name == "" {
|
|
| 198 |
+ return "", nil |
|
| 199 |
+ } |
|
| 200 |
+ var ref string |
|
| 201 |
+ switch outputTo.Kind {
|
|
| 202 |
+ case "DockerImage": |
|
| 203 |
+ ref = outputTo.Name |
|
| 204 |
+ case "ImageStream", "ImageStreamTag": |
|
| 205 |
+ // TODO(smarterclayton): security, ensure that the reference image stream is actually visible |
|
| 206 |
+ namespace := outputTo.Namespace |
|
| 207 |
+ if len(namespace) == 0 {
|
|
| 208 |
+ namespace = build.Namespace |
|
| 209 |
+ } |
|
| 210 |
+ |
|
| 211 |
+ var tag string |
|
| 212 |
+ streamName := outputTo.Name |
|
| 213 |
+ if outputTo.Kind == "ImageStreamTag" {
|
|
| 214 |
+ bits := strings.Split(outputTo.Name, ":") |
|
| 215 |
+ streamName = bits[0] |
|
| 216 |
+ tag = ":" + bits[1] |
|
| 217 |
+ } |
|
| 218 |
+ stream, err := bc.ImageStreamClient.GetImageStream(namespace, streamName) |
|
| 219 |
+ if err != nil {
|
|
| 220 |
+ if errors.IsNotFound(err) {
|
|
| 221 |
+ return "", fmt.Errorf("the referenced output ImageStream %s/%s does not exist", namespace, streamName)
|
|
| 222 |
+ } |
|
| 223 |
+ return "", fmt.Errorf("the referenced output ImageStream %s/%s could not be found by Build %s/%s: %v", namespace, streamName, build.Namespace, build.Name, err)
|
|
| 224 |
+ } |
|
| 225 |
+ if len(stream.Status.DockerImageRepository) == 0 {
|
|
| 226 |
+ e := fmt.Errorf("the ImageStream %s/%s cannot be used as the output for Build %s/%s because the integrated Docker registry is not configured and no external registry was defined", namespace, outputTo.Name, build.Namespace, build.Name)
|
|
| 227 |
+ bc.Recorder.Eventf(build, "invalidOutput", "Error starting build: %v", e) |
|
| 228 |
+ return "", e |
|
| 229 |
+ } |
|
| 230 |
+ ref = fmt.Sprintf("%s%s", stream.Status.DockerImageRepository, tag)
|
|
| 231 |
+ } |
|
| 232 |
+ return ref, nil |
|
| 233 |
+} |
|
| 234 |
+ |
|
| 193 | 235 |
// BuildPodController watches pods running builds and manages the build state |
| 194 | 236 |
type BuildPodController struct {
|
| 195 | 237 |
BuildStore cache.Store |
| ... | ... |
@@ -395,6 +395,9 @@ func TestHandleBuild(t *testing.T) {
|
| 395 | 395 |
if build.Spec.Output.To.Name != tc.outputSpec {
|
| 396 | 396 |
t.Errorf("(%d) expected build sent to strategy to have docker spec %s, got %s", i, tc.outputSpec, build.Spec.Output.To.Name)
|
| 397 | 397 |
} |
| 398 |
+ if build.Status.OutputDockerImageReference != tc.outputSpec {
|
|
| 399 |
+ t.Errorf("(%d) expected build status to have OutputDockerImageReference %s, got %s", i, tc.outputSpec, build.Status.OutputDockerImageReference)
|
|
| 400 |
+ } |
|
| 398 | 401 |
} |
| 399 | 402 |
} |
| 400 | 403 |
} |