also change to support an array of input images
| ... | ... |
@@ -14704,9 +14704,12 @@ |
| 14704 | 14704 |
"$ref": "v1.GitBuildSource", |
| 14705 | 14705 |
"description": "optional information about git build source" |
| 14706 | 14706 |
}, |
| 14707 |
- "image": {
|
|
| 14708 |
- "$ref": "v1.ImageSource", |
|
| 14709 |
- "description": "optional image build source. EXPERIMENTAL: This will be changing to an array of images in the near future and no migration/compatibility will be provided. Use at your own risk." |
|
| 14707 |
+ "images": {
|
|
| 14708 |
+ "type": "array", |
|
| 14709 |
+ "items": {
|
|
| 14710 |
+ "$ref": "v1.ImageSource" |
|
| 14711 |
+ }, |
|
| 14712 |
+ "description": "optional images for build source." |
|
| 14710 | 14713 |
}, |
| 14711 | 14714 |
"contextDir": {
|
| 14712 | 14715 |
"type": "string", |
| ... | ... |
@@ -1000,6 +1000,8 @@ _oc_new-build() |
| 1000 | 1000 |
flags+=("--show-all")
|
| 1001 | 1001 |
flags+=("-a")
|
| 1002 | 1002 |
flags+=("--sort-by=")
|
| 1003 |
+ flags+=("--source-image=")
|
|
| 1004 |
+ flags+=("--source-image-path=")
|
|
| 1003 | 1005 |
flags+=("--strategy=")
|
| 1004 | 1006 |
flags+=("--template=")
|
| 1005 | 1007 |
two_word_flags+=("-t")
|
| ... | ... |
@@ -5995,6 +5995,8 @@ _openshift_cli_new-build() |
| 5995 | 5995 |
flags+=("--show-all")
|
| 5996 | 5996 |
flags+=("-a")
|
| 5997 | 5997 |
flags+=("--sort-by=")
|
| 5998 |
+ flags+=("--source-image=")
|
|
| 5999 |
+ flags+=("--source-image-path=")
|
|
| 5998 | 6000 |
flags+=("--strategy=")
|
| 5999 | 6001 |
flags+=("--template=")
|
| 6000 | 6002 |
two_word_flags+=("-t")
|
| ... | ... |
@@ -590,6 +590,9 @@ Create a new build configuration |
| 590 | 590 |
|
| 591 | 591 |
# Create a build config from a remote repository and inject the npmrc into a build |
| 592 | 592 |
$ oc new-build https://github.com/openshift/ruby-hello-world --build-secret npmrc:.npmrc |
| 593 |
+ |
|
| 594 |
+ # Create a build config that gets its input from a remote repository and another Docker image |
|
| 595 |
+ $ oc new-build https://github.com/openshift/ruby-hello-world --image-source=openshift/jenkins-1-centos7 --image-source-path=/var/lib/jenkins:/tmp |
|
| 593 | 596 |
---- |
| 594 | 597 |
==== |
| 595 | 598 |
|
| ... | ... |
@@ -966,13 +966,15 @@ func deepCopy_api_BuildSource(in buildapi.BuildSource, out *buildapi.BuildSource |
| 966 | 966 |
} else {
|
| 967 | 967 |
out.Git = nil |
| 968 | 968 |
} |
| 969 |
- if in.Image != nil {
|
|
| 970 |
- out.Image = new(buildapi.ImageSource) |
|
| 971 |
- if err := deepCopy_api_ImageSource(*in.Image, out.Image, c); err != nil {
|
|
| 972 |
- return err |
|
| 969 |
+ if in.Images != nil {
|
|
| 970 |
+ out.Images = make([]buildapi.ImageSource, len(in.Images)) |
|
| 971 |
+ for i := range in.Images {
|
|
| 972 |
+ if err := deepCopy_api_ImageSource(in.Images[i], &out.Images[i], c); err != nil {
|
|
| 973 |
+ return err |
|
| 974 |
+ } |
|
| 973 | 975 |
} |
| 974 | 976 |
} else {
|
| 975 |
- out.Image = nil |
|
| 977 |
+ out.Images = nil |
|
| 976 | 978 |
} |
| 977 | 979 |
out.ContextDir = in.ContextDir |
| 978 | 980 |
if in.SourceSecret != nil {
|
| ... | ... |
@@ -1418,13 +1418,15 @@ func autoconvert_api_BuildSource_To_v1_BuildSource(in *buildapi.BuildSource, out |
| 1418 | 1418 |
} else {
|
| 1419 | 1419 |
out.Git = nil |
| 1420 | 1420 |
} |
| 1421 |
- if in.Image != nil {
|
|
| 1422 |
- out.Image = new(apiv1.ImageSource) |
|
| 1423 |
- if err := convert_api_ImageSource_To_v1_ImageSource(in.Image, out.Image, s); err != nil {
|
|
| 1424 |
- return err |
|
| 1421 |
+ if in.Images != nil {
|
|
| 1422 |
+ out.Images = make([]apiv1.ImageSource, len(in.Images)) |
|
| 1423 |
+ for i := range in.Images {
|
|
| 1424 |
+ if err := convert_api_ImageSource_To_v1_ImageSource(&in.Images[i], &out.Images[i], s); err != nil {
|
|
| 1425 |
+ return err |
|
| 1426 |
+ } |
|
| 1425 | 1427 |
} |
| 1426 | 1428 |
} else {
|
| 1427 |
- out.Image = nil |
|
| 1429 |
+ out.Images = nil |
|
| 1428 | 1430 |
} |
| 1429 | 1431 |
out.ContextDir = in.ContextDir |
| 1430 | 1432 |
if in.SourceSecret != nil {
|
| ... | ... |
@@ -2210,13 +2212,15 @@ func autoconvert_v1_BuildSource_To_api_BuildSource(in *apiv1.BuildSource, out *b |
| 2210 | 2210 |
} else {
|
| 2211 | 2211 |
out.Git = nil |
| 2212 | 2212 |
} |
| 2213 |
- if in.Image != nil {
|
|
| 2214 |
- out.Image = new(buildapi.ImageSource) |
|
| 2215 |
- if err := convert_v1_ImageSource_To_api_ImageSource(in.Image, out.Image, s); err != nil {
|
|
| 2216 |
- return err |
|
| 2213 |
+ if in.Images != nil {
|
|
| 2214 |
+ out.Images = make([]buildapi.ImageSource, len(in.Images)) |
|
| 2215 |
+ for i := range in.Images {
|
|
| 2216 |
+ if err := convert_v1_ImageSource_To_api_ImageSource(&in.Images[i], &out.Images[i], s); err != nil {
|
|
| 2217 |
+ return err |
|
| 2218 |
+ } |
|
| 2217 | 2219 |
} |
| 2218 | 2220 |
} else {
|
| 2219 |
- out.Image = nil |
|
| 2221 |
+ out.Images = nil |
|
| 2220 | 2222 |
} |
| 2221 | 2223 |
out.ContextDir = in.ContextDir |
| 2222 | 2224 |
if in.SourceSecret != nil {
|
| ... | ... |
@@ -991,13 +991,15 @@ func deepCopy_v1_BuildSource(in apiv1.BuildSource, out *apiv1.BuildSource, c *co |
| 991 | 991 |
} else {
|
| 992 | 992 |
out.Git = nil |
| 993 | 993 |
} |
| 994 |
- if in.Image != nil {
|
|
| 995 |
- out.Image = new(apiv1.ImageSource) |
|
| 996 |
- if err := deepCopy_v1_ImageSource(*in.Image, out.Image, c); err != nil {
|
|
| 997 |
- return err |
|
| 994 |
+ if in.Images != nil {
|
|
| 995 |
+ out.Images = make([]apiv1.ImageSource, len(in.Images)) |
|
| 996 |
+ for i := range in.Images {
|
|
| 997 |
+ if err := deepCopy_v1_ImageSource(in.Images[i], &out.Images[i], c); err != nil {
|
|
| 998 |
+ return err |
|
| 999 |
+ } |
|
| 998 | 1000 |
} |
| 999 | 1001 |
} else {
|
| 1000 |
- out.Image = nil |
|
| 1002 |
+ out.Images = nil |
|
| 1001 | 1003 |
} |
| 1002 | 1004 |
out.ContextDir = in.ContextDir |
| 1003 | 1005 |
if in.SourceSecret != nil {
|
| ... | ... |
@@ -1427,13 +1427,15 @@ func autoconvert_api_BuildSource_To_v1beta3_BuildSource(in *buildapi.BuildSource |
| 1427 | 1427 |
} else {
|
| 1428 | 1428 |
out.Git = nil |
| 1429 | 1429 |
} |
| 1430 |
- if in.Image != nil {
|
|
| 1431 |
- out.Image = new(apiv1beta3.ImageSource) |
|
| 1432 |
- if err := convert_api_ImageSource_To_v1beta3_ImageSource(in.Image, out.Image, s); err != nil {
|
|
| 1433 |
- return err |
|
| 1430 |
+ if in.Images != nil {
|
|
| 1431 |
+ out.Images = make([]apiv1beta3.ImageSource, len(in.Images)) |
|
| 1432 |
+ for i := range in.Images {
|
|
| 1433 |
+ if err := convert_api_ImageSource_To_v1beta3_ImageSource(&in.Images[i], &out.Images[i], s); err != nil {
|
|
| 1434 |
+ return err |
|
| 1435 |
+ } |
|
| 1434 | 1436 |
} |
| 1435 | 1437 |
} else {
|
| 1436 |
- out.Image = nil |
|
| 1438 |
+ out.Images = nil |
|
| 1437 | 1439 |
} |
| 1438 | 1440 |
out.ContextDir = in.ContextDir |
| 1439 | 1441 |
if in.SourceSecret != nil {
|
| ... | ... |
@@ -2219,13 +2221,15 @@ func autoconvert_v1beta3_BuildSource_To_api_BuildSource(in *apiv1beta3.BuildSour |
| 2219 | 2219 |
} else {
|
| 2220 | 2220 |
out.Git = nil |
| 2221 | 2221 |
} |
| 2222 |
- if in.Image != nil {
|
|
| 2223 |
- out.Image = new(buildapi.ImageSource) |
|
| 2224 |
- if err := convert_v1beta3_ImageSource_To_api_ImageSource(in.Image, out.Image, s); err != nil {
|
|
| 2225 |
- return err |
|
| 2222 |
+ if in.Images != nil {
|
|
| 2223 |
+ out.Images = make([]buildapi.ImageSource, len(in.Images)) |
|
| 2224 |
+ for i := range in.Images {
|
|
| 2225 |
+ if err := convert_v1beta3_ImageSource_To_api_ImageSource(&in.Images[i], &out.Images[i], s); err != nil {
|
|
| 2226 |
+ return err |
|
| 2227 |
+ } |
|
| 2226 | 2228 |
} |
| 2227 | 2229 |
} else {
|
| 2228 |
- out.Image = nil |
|
| 2230 |
+ out.Images = nil |
|
| 2229 | 2231 |
} |
| 2230 | 2232 |
out.ContextDir = in.ContextDir |
| 2231 | 2233 |
if in.SourceSecret != nil {
|
| ... | ... |
@@ -999,13 +999,15 @@ func deepCopy_v1beta3_BuildSource(in apiv1beta3.BuildSource, out *apiv1beta3.Bui |
| 999 | 999 |
} else {
|
| 1000 | 1000 |
out.Git = nil |
| 1001 | 1001 |
} |
| 1002 |
- if in.Image != nil {
|
|
| 1003 |
- out.Image = new(apiv1beta3.ImageSource) |
|
| 1004 |
- if err := deepCopy_v1beta3_ImageSource(*in.Image, out.Image, c); err != nil {
|
|
| 1005 |
- return err |
|
| 1002 |
+ if in.Images != nil {
|
|
| 1003 |
+ out.Images = make([]apiv1beta3.ImageSource, len(in.Images)) |
|
| 1004 |
+ for i := range in.Images {
|
|
| 1005 |
+ if err := deepCopy_v1beta3_ImageSource(in.Images[i], &out.Images[i], c); err != nil {
|
|
| 1006 |
+ return err |
|
| 1007 |
+ } |
|
| 1006 | 1008 |
} |
| 1007 | 1009 |
} else {
|
| 1008 |
- out.Image = nil |
|
| 1010 |
+ out.Images = nil |
|
| 1009 | 1011 |
} |
| 1010 | 1012 |
out.ContextDir = in.ContextDir |
| 1011 | 1013 |
if in.SourceSecret != nil {
|
| ... | ... |
@@ -188,10 +188,8 @@ type BuildSource struct {
|
| 188 | 188 |
// Git contains optional information about git build source |
| 189 | 189 |
Git *GitBuildSource |
| 190 | 190 |
|
| 191 |
- // Image describes an image to be used to provide source for the build |
|
| 192 |
- // EXPERIMENTAL. This will be changing to an array of images in the near future |
|
| 193 |
- // and no migration/compatibility will be provided. Use at your own risk. |
|
| 194 |
- Image *ImageSource |
|
| 191 |
+ // Images describes a set of images to be used to provide source for the build |
|
| 192 |
+ Images []ImageSource |
|
| 195 | 193 |
|
| 196 | 194 |
// ContextDir specifies the sub-directory where the source code for the application exists. |
| 197 | 195 |
// This allows to have buildable sources in directory other than root of |
| ... | ... |
@@ -160,10 +160,8 @@ type BuildSource struct {
|
| 160 | 160 |
// Git contains optional information about git build source |
| 161 | 161 |
Git *GitBuildSource `json:"git,omitempty" description:"optional information about git build source"` |
| 162 | 162 |
|
| 163 |
- // Image describes an image to be used to provide source for the build |
|
| 164 |
- // EXPERIMENTAL. This will be changing to an array of images in the near future |
|
| 165 |
- // and no migration/compatibility will be provided. Use at your own risk. |
|
| 166 |
- Image *ImageSource `json:"image,omitempty" description:"optional image build source. EXPERIMENTAL: This will be changing to an array of images in the near future and no migration/compatibility will be provided. Use at your own risk."` |
|
| 163 |
+ // Images describes a set of images to be used to provide source for the build |
|
| 164 |
+ Images []ImageSource `json:"images,omitempty" description:"optional images for build source."` |
|
| 167 | 165 |
|
| 168 | 166 |
// ContextDir specifies the sub-directory where the source code for the application exists. |
| 169 | 167 |
// This allows to have buildable sources in directory other than root of |
| ... | ... |
@@ -156,10 +156,8 @@ type BuildSource struct {
|
| 156 | 156 |
// Git contains optional information about git build source. |
| 157 | 157 |
Git *GitBuildSource `json:"git,omitempty"` |
| 158 | 158 |
|
| 159 |
- // Image describes an image to be used to provide source for the build |
|
| 160 |
- // EXPERIMENTAL. This will be changing to an array of images in the near future |
|
| 161 |
- // and no migration/compatibility will be provided. Use at your own risk. |
|
| 162 |
- Image *ImageSource `json:"image,omitempty" description:"optional image build source. EXPERIMENTAL: This will be changing to an array of images in the near future and no migration/compatibility will be provided. Use at your own risk."` |
|
| 159 |
+ // Images describes a set of images to be used to provide source for the build |
|
| 160 |
+ Images []ImageSource `json:"images,omitempty" description:"optional images for build source."` |
|
| 163 | 161 |
|
| 164 | 162 |
// Specify the sub-directory where the source code for the application exists. |
| 165 | 163 |
// This allows to have buildable sources in directory other than root of |
| ... | ... |
@@ -159,8 +159,10 @@ func validateSource(input *buildapi.BuildSource, isCustomStrategy, isDockerStrat |
| 159 | 159 |
if input.Dockerfile != nil {
|
| 160 | 160 |
allErrs = append(allErrs, validateDockerfile(*input.Dockerfile)...) |
| 161 | 161 |
} |
| 162 |
- if input.Image != nil {
|
|
| 163 |
- allErrs = append(allErrs, validateImageSource(input.Image).Prefix("image")...)
|
|
| 162 |
+ if input.Images != nil {
|
|
| 163 |
+ for i, image := range input.Images {
|
|
| 164 |
+ allErrs = append(allErrs, validateImageSource(image).PrefixIndex(i).Prefix("images")...)
|
|
| 165 |
+ } |
|
| 164 | 166 |
} |
| 165 | 167 |
|
| 166 | 168 |
allErrs = append(allErrs, validateSecrets(input.Secrets, isDockerStrategy).Prefix("secrets")...)
|
| ... | ... |
@@ -244,7 +246,7 @@ func validateSecrets(secrets []buildapi.SecretBuildSource, isDockerStrategy bool |
| 244 | 244 |
return allErrs |
| 245 | 245 |
} |
| 246 | 246 |
|
| 247 |
-func validateImageSource(imageSource *buildapi.ImageSource) fielderrors.ValidationErrorList {
|
|
| 247 |
+func validateImageSource(imageSource buildapi.ImageSource) fielderrors.ValidationErrorList {
|
|
| 248 | 248 |
allErrs := fielderrors.ValidationErrorList{}
|
| 249 | 249 |
allErrs = append(allErrs, validateFromImageReference(&imageSource.From).Prefix("from")...)
|
| 250 | 250 |
if imageSource.PullSecret != nil {
|
| ... | ... |
@@ -253,9 +255,32 @@ func validateImageSource(imageSource *buildapi.ImageSource) fielderrors.Validati |
| 253 | 253 |
if len(imageSource.Paths) == 0 {
|
| 254 | 254 |
allErrs = append(allErrs, fielderrors.NewFieldRequired("paths"))
|
| 255 | 255 |
} |
| 256 |
+ for i, path := range imageSource.Paths {
|
|
| 257 |
+ allErrs = append(allErrs, validateImageSourcePath(path).PrefixIndex(i).Prefix("paths")...)
|
|
| 258 |
+ |
|
| 259 |
+ } |
|
| 256 | 260 |
return allErrs |
| 257 | 261 |
} |
| 258 | 262 |
|
| 263 |
+func validateImageSourcePath(imagePath buildapi.ImageSourcePath) fielderrors.ValidationErrorList {
|
|
| 264 |
+ allErrs := fielderrors.ValidationErrorList{}
|
|
| 265 |
+ if len(imagePath.SourcePath) == 0 {
|
|
| 266 |
+ allErrs = append(allErrs, fielderrors.NewFieldRequired("sourcePath"))
|
|
| 267 |
+ } |
|
| 268 |
+ if len(imagePath.DestinationDir) == 0 {
|
|
| 269 |
+ allErrs = append(allErrs, fielderrors.NewFieldRequired("destinationDir"))
|
|
| 270 |
+ } |
|
| 271 |
+ if !filepath.IsAbs(imagePath.SourcePath) {
|
|
| 272 |
+ allErrs = append(allErrs, fielderrors.NewFieldInvalid("sourcePath", imagePath.SourcePath, "must be an absolute path"))
|
|
| 273 |
+ } |
|
| 274 |
+ if filepath.IsAbs(imagePath.DestinationDir) {
|
|
| 275 |
+ allErrs = append(allErrs, fielderrors.NewFieldInvalid("destinationDir", imagePath.DestinationDir, "must be a relative path"))
|
|
| 276 |
+ } |
|
| 277 |
+ if strings.HasPrefix(path.Clean(imagePath.DestinationDir), "..") {
|
|
| 278 |
+ allErrs = append(allErrs, fielderrors.NewFieldInvalid("destinationDir", imagePath.DestinationDir, "destination dir cannot start with '..'"))
|
|
| 279 |
+ } |
|
| 280 |
+ return allErrs |
|
| 281 |
+} |
|
| 259 | 282 |
func validateBinarySource(source *buildapi.BinaryBuildSource) fielderrors.ValidationErrorList {
|
| 260 | 283 |
allErrs := fielderrors.ValidationErrorList{}
|
| 261 | 284 |
if len(source.AsFile) != 0 {
|
| ... | ... |
@@ -785,15 +785,29 @@ func TestValidateSource(t *testing.T) {
|
| 785 | 785 |
{
|
| 786 | 786 |
ok: true, |
| 787 | 787 |
source: &buildapi.BuildSource{
|
| 788 |
- Image: &buildapi.ImageSource{
|
|
| 789 |
- From: kapi.ObjectReference{
|
|
| 790 |
- Kind: "ImageStreamTag", |
|
| 791 |
- Name: "my-image:latest", |
|
| 788 |
+ Images: []buildapi.ImageSource{
|
|
| 789 |
+ {
|
|
| 790 |
+ From: kapi.ObjectReference{
|
|
| 791 |
+ Kind: "ImageStreamTag", |
|
| 792 |
+ Name: "my-image:latest", |
|
| 793 |
+ }, |
|
| 794 |
+ Paths: []buildapi.ImageSourcePath{
|
|
| 795 |
+ {
|
|
| 796 |
+ SourcePath: "/some/path", |
|
| 797 |
+ DestinationDir: "test/dir", |
|
| 798 |
+ }, |
|
| 799 |
+ }, |
|
| 792 | 800 |
}, |
| 793 |
- Paths: []buildapi.ImageSourcePath{
|
|
| 794 |
- {
|
|
| 795 |
- SourcePath: "/some/path", |
|
| 796 |
- DestinationDir: "test/dir", |
|
| 801 |
+ {
|
|
| 802 |
+ From: kapi.ObjectReference{
|
|
| 803 |
+ Kind: "ImageStreamTag", |
|
| 804 |
+ Name: "my-image:latest", |
|
| 805 |
+ }, |
|
| 806 |
+ Paths: []buildapi.ImageSourcePath{
|
|
| 807 |
+ {
|
|
| 808 |
+ SourcePath: "/some/path", |
|
| 809 |
+ DestinationDir: "test/dir", |
|
| 810 |
+ }, |
|
| 797 | 811 |
}, |
| 798 | 812 |
}, |
| 799 | 813 |
}, |
| ... | ... |
@@ -802,52 +816,121 @@ func TestValidateSource(t *testing.T) {
|
| 802 | 802 |
// 16 |
| 803 | 803 |
{
|
| 804 | 804 |
t: fielderrors.ValidationErrorTypeRequired, |
| 805 |
- path: "image.paths", |
|
| 805 |
+ path: "images[0].paths", |
|
| 806 | 806 |
source: &buildapi.BuildSource{
|
| 807 |
- Image: &buildapi.ImageSource{
|
|
| 808 |
- From: kapi.ObjectReference{
|
|
| 809 |
- Kind: "ImageStreamTag", |
|
| 810 |
- Name: "my-image:latest", |
|
| 807 |
+ Images: []buildapi.ImageSource{
|
|
| 808 |
+ {
|
|
| 809 |
+ From: kapi.ObjectReference{
|
|
| 810 |
+ Kind: "ImageStreamTag", |
|
| 811 |
+ Name: "my-image:latest", |
|
| 812 |
+ }, |
|
| 811 | 813 |
}, |
| 812 | 814 |
}, |
| 813 | 815 |
}, |
| 814 | 816 |
}, |
| 815 |
- // 17 |
|
| 817 |
+ // 17 - destinationdir is not relative. |
|
| 816 | 818 |
{
|
| 817 | 819 |
t: fielderrors.ValidationErrorTypeInvalid, |
| 818 |
- path: "image.from.kind", |
|
| 820 |
+ path: "images[0].paths[0].destinationDir", |
|
| 819 | 821 |
source: &buildapi.BuildSource{
|
| 820 |
- Image: &buildapi.ImageSource{
|
|
| 821 |
- From: kapi.ObjectReference{
|
|
| 822 |
- Kind: "InvalidKind", |
|
| 823 |
- Name: "my-image:latest", |
|
| 824 |
- }, |
|
| 825 |
- Paths: []buildapi.ImageSourcePath{
|
|
| 826 |
- {
|
|
| 827 |
- SourcePath: "/some/path", |
|
| 828 |
- DestinationDir: "test/dir", |
|
| 822 |
+ Images: []buildapi.ImageSource{
|
|
| 823 |
+ {
|
|
| 824 |
+ From: kapi.ObjectReference{
|
|
| 825 |
+ Kind: "ImageStreamTag", |
|
| 826 |
+ Name: "my-image:latest", |
|
| 827 |
+ }, |
|
| 828 |
+ Paths: []buildapi.ImageSourcePath{
|
|
| 829 |
+ {
|
|
| 830 |
+ SourcePath: "/some/path", |
|
| 831 |
+ DestinationDir: "/test/dir", |
|
| 832 |
+ }, |
|
| 829 | 833 |
}, |
| 830 | 834 |
}, |
| 831 | 835 |
}, |
| 832 | 836 |
}, |
| 833 | 837 |
}, |
| 834 |
- // 18 |
|
| 838 |
+ // 18 - sourcepath is not absolute. |
|
| 835 | 839 |
{
|
| 836 |
- t: fielderrors.ValidationErrorTypeRequired, |
|
| 837 |
- path: "image.pullSecret.name", |
|
| 840 |
+ t: fielderrors.ValidationErrorTypeInvalid, |
|
| 841 |
+ path: "images[0].paths[0].sourcePath", |
|
| 838 | 842 |
source: &buildapi.BuildSource{
|
| 839 |
- Image: &buildapi.ImageSource{
|
|
| 840 |
- From: kapi.ObjectReference{
|
|
| 841 |
- Kind: "DockerImage", |
|
| 842 |
- Name: "my-image:latest", |
|
| 843 |
+ Images: []buildapi.ImageSource{
|
|
| 844 |
+ {
|
|
| 845 |
+ From: kapi.ObjectReference{
|
|
| 846 |
+ Kind: "ImageStreamTag", |
|
| 847 |
+ Name: "my-image:latest", |
|
| 848 |
+ }, |
|
| 849 |
+ Paths: []buildapi.ImageSourcePath{
|
|
| 850 |
+ {
|
|
| 851 |
+ SourcePath: "some/path", |
|
| 852 |
+ DestinationDir: "test/dir", |
|
| 853 |
+ }, |
|
| 854 |
+ }, |
|
| 843 | 855 |
}, |
| 844 |
- PullSecret: &kapi.LocalObjectReference{
|
|
| 845 |
- Name: "", |
|
| 856 |
+ }, |
|
| 857 |
+ }, |
|
| 858 |
+ }, |
|
| 859 |
+ // 19 - destinationdir backsteps above basedir |
|
| 860 |
+ {
|
|
| 861 |
+ t: fielderrors.ValidationErrorTypeInvalid, |
|
| 862 |
+ path: "images[0].paths[0].destinationDir", |
|
| 863 |
+ source: &buildapi.BuildSource{
|
|
| 864 |
+ Images: []buildapi.ImageSource{
|
|
| 865 |
+ {
|
|
| 866 |
+ From: kapi.ObjectReference{
|
|
| 867 |
+ Kind: "ImageStreamTag", |
|
| 868 |
+ Name: "my-image:latest", |
|
| 869 |
+ }, |
|
| 870 |
+ Paths: []buildapi.ImageSourcePath{
|
|
| 871 |
+ {
|
|
| 872 |
+ SourcePath: "/some/path", |
|
| 873 |
+ DestinationDir: "test/../../dir", |
|
| 874 |
+ }, |
|
| 875 |
+ }, |
|
| 846 | 876 |
}, |
| 847 |
- Paths: []buildapi.ImageSourcePath{
|
|
| 848 |
- {
|
|
| 849 |
- SourcePath: "/some/path", |
|
| 850 |
- DestinationDir: "test/dir", |
|
| 877 |
+ }, |
|
| 878 |
+ }, |
|
| 879 |
+ }, |
|
| 880 |
+ // 20 |
|
| 881 |
+ {
|
|
| 882 |
+ t: fielderrors.ValidationErrorTypeInvalid, |
|
| 883 |
+ path: "images[0].from.kind", |
|
| 884 |
+ source: &buildapi.BuildSource{
|
|
| 885 |
+ Images: []buildapi.ImageSource{
|
|
| 886 |
+ {
|
|
| 887 |
+ From: kapi.ObjectReference{
|
|
| 888 |
+ Kind: "InvalidKind", |
|
| 889 |
+ Name: "my-image:latest", |
|
| 890 |
+ }, |
|
| 891 |
+ Paths: []buildapi.ImageSourcePath{
|
|
| 892 |
+ {
|
|
| 893 |
+ SourcePath: "/some/path", |
|
| 894 |
+ DestinationDir: "test/dir", |
|
| 895 |
+ }, |
|
| 896 |
+ }, |
|
| 897 |
+ }, |
|
| 898 |
+ }, |
|
| 899 |
+ }, |
|
| 900 |
+ }, |
|
| 901 |
+ // 21 |
|
| 902 |
+ {
|
|
| 903 |
+ t: fielderrors.ValidationErrorTypeRequired, |
|
| 904 |
+ path: "images[0].pullSecret.name", |
|
| 905 |
+ source: &buildapi.BuildSource{
|
|
| 906 |
+ Images: []buildapi.ImageSource{
|
|
| 907 |
+ {
|
|
| 908 |
+ From: kapi.ObjectReference{
|
|
| 909 |
+ Kind: "DockerImage", |
|
| 910 |
+ Name: "my-image:latest", |
|
| 911 |
+ }, |
|
| 912 |
+ PullSecret: &kapi.LocalObjectReference{
|
|
| 913 |
+ Name: "", |
|
| 914 |
+ }, |
|
| 915 |
+ Paths: []buildapi.ImageSourcePath{
|
|
| 916 |
+ {
|
|
| 917 |
+ SourcePath: "/some/path", |
|
| 918 |
+ DestinationDir: "test/dir", |
|
| 919 |
+ }, |
|
| 851 | 920 |
}, |
| 852 | 921 |
}, |
| 853 | 922 |
}, |
| ... | ... |
@@ -22,7 +22,7 @@ const ( |
| 22 | 22 |
defaultRegistryServer = "https://index.docker.io/v1/" |
| 23 | 23 |
PushAuthType = "PUSH_DOCKERCFG_PATH" |
| 24 | 24 |
PullAuthType = "PULL_DOCKERCFG_PATH" |
| 25 |
- PullSourceAuthType = "PULL_SOURCE_DOCKERCFG_PATH" |
|
| 25 |
+ PullSourceAuthType = "PULL_SOURCE_DOCKERCFG_PATH_" |
|
| 26 | 26 |
) |
| 27 | 27 |
|
| 28 | 28 |
// Helper contains all the valid config options for reading the local dockercfg file |
| ... | ... |
@@ -39,6 +39,7 @@ type DockerClient interface {
|
| 39 | 39 |
DownloadFromContainer(id string, opts docker.DownloadFromContainerOptions) error |
| 40 | 40 |
PullImage(opts docker.PullImageOptions, auth docker.AuthConfiguration) error |
| 41 | 41 |
RemoveContainer(opts docker.RemoveContainerOptions) error |
| 42 |
+ InspectImage(name string) (*docker.Image, error) |
|
| 42 | 43 |
} |
| 43 | 44 |
|
| 44 | 45 |
// pushImage pushes a docker image to the registry specified in its tag. |
| ... | ... |
@@ -46,7 +46,9 @@ func (d *FakeDocker) PullImage(opts docker.PullImageOptions, auth docker.AuthCon |
| 46 | 46 |
func (d *FakeDocker) RemoveContainer(opts docker.RemoveContainerOptions) error {
|
| 47 | 47 |
return nil |
| 48 | 48 |
} |
| 49 |
- |
|
| 49 |
+func (d *FakeDocker) InspectImage(name string) (*docker.Image, error) {
|
|
| 50 |
+ return nil, nil |
|
| 51 |
+} |
|
| 50 | 52 |
func TestDockerPush(t *testing.T) {
|
| 51 | 53 |
verifyFunc := func(opts docker.PushImageOptions, auth docker.AuthConfiguration) error {
|
| 52 | 54 |
if opts.Name != "test/image" {
|
| ... | ... |
@@ -65,10 +65,22 @@ func fetchSource(dockerClient DockerClient, dir string, build *api.Build, urlTim |
| 65 | 65 |
} |
| 66 | 66 |
} |
| 67 | 67 |
|
| 68 |
+ forcePull := false |
|
| 69 |
+ switch {
|
|
| 70 |
+ case build.Spec.Strategy.SourceStrategy != nil: |
|
| 71 |
+ forcePull = build.Spec.Strategy.SourceStrategy.ForcePull |
|
| 72 |
+ case build.Spec.Strategy.DockerStrategy != nil: |
|
| 73 |
+ forcePull = build.Spec.Strategy.DockerStrategy.ForcePull |
|
| 74 |
+ case build.Spec.Strategy.CustomStrategy != nil: |
|
| 75 |
+ forcePull = build.Spec.Strategy.CustomStrategy.ForcePull |
|
| 76 |
+ } |
|
| 68 | 77 |
// extract source from an Image if specified |
| 69 |
- if build.Spec.Source.Image != nil {
|
|
| 70 |
- // fetch image source |
|
| 71 |
- err := extractSourceFromImage(dockerClient, build.Spec.Source.Image.From.Name, dir, build.Spec.Source.Image.Paths) |
|
| 78 |
+ for i, image := range build.Spec.Source.Images {
|
|
| 79 |
+ imageSecretIndex := i |
|
| 80 |
+ if image.PullSecret == nil {
|
|
| 81 |
+ imageSecretIndex = -1 |
|
| 82 |
+ } |
|
| 83 |
+ err := extractSourceFromImage(dockerClient, image.From.Name, dir, imageSecretIndex, image.Paths, forcePull) |
|
| 72 | 84 |
if err != nil {
|
| 73 | 85 |
return nil, err |
| 74 | 86 |
} |
| ... | ... |
@@ -256,16 +268,43 @@ func copyImageSource(dockerClient DockerClient, containerID, sourceDir, destDir |
| 256 | 256 |
return tarHelper.ExtractTarStreamWithLogging(destDir, file, tarOutput) |
| 257 | 257 |
} |
| 258 | 258 |
|
| 259 |
-func extractSourceFromImage(dockerClient DockerClient, image, buildDir string, paths []api.ImageSourcePath) error {
|
|
| 259 |
+func extractSourceFromImage(dockerClient DockerClient, image, buildDir string, imageSecretIndex int, paths []api.ImageSourcePath, forcePull bool) error {
|
|
| 260 | 260 |
glog.V(4).Infof("Extracting image source from %s", image)
|
| 261 | 261 |
|
| 262 |
- // Pre-pull image if a secret is specified |
|
| 263 |
- pullSecret := os.Getenv(dockercfg.PullSourceAuthType) |
|
| 264 |
- if len(pullSecret) > 0 {
|
|
| 265 |
- dockerAuth, present := dockercfg.NewHelper().GetDockerAuth(image, dockercfg.PullSourceAuthType) |
|
| 266 |
- if present {
|
|
| 267 |
- dockerClient.PullImage(docker.PullImageOptions{Repository: image}, dockerAuth)
|
|
| 262 |
+ dockerAuth := docker.AuthConfiguration{}
|
|
| 263 |
+ if imageSecretIndex != -1 {
|
|
| 264 |
+ pullSecret := os.Getenv(fmt.Sprintf("%s%d", dockercfg.PullSourceAuthType, imageSecretIndex))
|
|
| 265 |
+ if len(pullSecret) > 0 {
|
|
| 266 |
+ authPresent := false |
|
| 267 |
+ dockerAuth, authPresent = dockercfg.NewHelper().GetDockerAuth(image, fmt.Sprintf("%s%d", dockercfg.PullSourceAuthType, imageSecretIndex))
|
|
| 268 |
+ if authPresent {
|
|
| 269 |
+ glog.V(5).Infof("Registry server Address: %s", dockerAuth.ServerAddress)
|
|
| 270 |
+ glog.V(5).Infof("Registry server User Name: %s", dockerAuth.Username)
|
|
| 271 |
+ glog.V(5).Infof("Registry server Email: %s", dockerAuth.Email)
|
|
| 272 |
+ passwordPresent := "<<empty>>" |
|
| 273 |
+ if len(dockerAuth.Password) > 0 {
|
|
| 274 |
+ passwordPresent = "<<non-empty>>" |
|
| 275 |
+ } |
|
| 276 |
+ glog.V(5).Infof("Registry server Password: %s", passwordPresent)
|
|
| 277 |
+ } |
|
| 278 |
+ } |
|
| 279 |
+ } |
|
| 280 |
+ |
|
| 281 |
+ exists := true |
|
| 282 |
+ if !forcePull {
|
|
| 283 |
+ _, err := dockerClient.InspectImage(image) |
|
| 284 |
+ if err != nil && err == docker.ErrNoSuchImage {
|
|
| 285 |
+ exists = false |
|
| 286 |
+ } else if err != nil {
|
|
| 287 |
+ return err |
|
| 288 |
+ } |
|
| 289 |
+ } |
|
| 290 |
+ |
|
| 291 |
+ if !exists || forcePull {
|
|
| 292 |
+ if err := dockerClient.PullImage(docker.PullImageOptions{Repository: image}, dockerAuth); err != nil {
|
|
| 293 |
+ return fmt.Errorf("error pulling image %v: %v", image, err)
|
|
| 268 | 294 |
} |
| 295 |
+ |
|
| 269 | 296 |
} |
| 270 | 297 |
|
| 271 | 298 |
// Create container to copy from |
| ... | ... |
@@ -51,6 +51,9 @@ func (client testDockerClient) PullImage(opts docker.PullImageOptions, auth dock |
| 51 | 51 |
func (client testDockerClient) RemoveContainer(opts docker.RemoveContainerOptions) error {
|
| 52 | 52 |
return nil |
| 53 | 53 |
} |
| 54 |
+func (client testDockerClient) InspectImage(name string) (*docker.Image, error) {
|
|
| 55 |
+ return nil, nil |
|
| 56 |
+} |
|
| 54 | 57 |
|
| 55 | 58 |
type testStiBuilderFactory struct {
|
| 56 | 59 |
getStrategyErr error |
| ... | ... |
@@ -92,11 +92,7 @@ func (bs *CustomBuildStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod, |
| 92 | 92 |
|
| 93 | 93 |
if strategy.ExposeDockerSocket {
|
| 94 | 94 |
setupDockerSocket(pod) |
| 95 |
- var sourceImageSecret *kapi.LocalObjectReference |
|
| 96 |
- if build.Spec.Source.Image != nil {
|
|
| 97 |
- sourceImageSecret = build.Spec.Source.Image.PullSecret |
|
| 98 |
- } |
|
| 99 |
- setupDockerSecrets(pod, build.Spec.Output.PushSecret, strategy.PullSecret, sourceImageSecret) |
|
| 95 |
+ setupDockerSecrets(pod, build.Spec.Output.PushSecret, strategy.PullSecret, build.Spec.Source.Images) |
|
| 100 | 96 |
} |
| 101 | 97 |
setupSourceSecrets(pod, build.Spec.Source.SourceSecret) |
| 102 | 98 |
setupSecrets(pod, build.Spec.Source.Secrets) |
| ... | ... |
@@ -77,11 +77,7 @@ func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod, |
| 77 | 77 |
} |
| 78 | 78 |
|
| 79 | 79 |
setupDockerSocket(pod) |
| 80 |
- var sourceImageSecret *kapi.LocalObjectReference |
|
| 81 |
- if build.Spec.Source.Image != nil {
|
|
| 82 |
- sourceImageSecret = build.Spec.Source.Image.PullSecret |
|
| 83 |
- } |
|
| 84 |
- setupDockerSecrets(pod, build.Spec.Output.PushSecret, strategy.PullSecret, sourceImageSecret) |
|
| 80 |
+ setupDockerSecrets(pod, build.Spec.Output.PushSecret, strategy.PullSecret, build.Spec.Source.Images) |
|
| 85 | 81 |
setupSourceSecrets(pod, build.Spec.Source.SourceSecret) |
| 86 | 82 |
setupSecrets(pod, build.Spec.Source.Secrets) |
| 87 | 83 |
|
| ... | ... |
@@ -99,11 +99,7 @@ func (bs *SourceBuildStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod, |
| 99 | 99 |
} |
| 100 | 100 |
|
| 101 | 101 |
setupDockerSocket(pod) |
| 102 |
- var sourceImageSecret *kapi.LocalObjectReference |
|
| 103 |
- if build.Spec.Source.Image != nil {
|
|
| 104 |
- sourceImageSecret = build.Spec.Source.Image.PullSecret |
|
| 105 |
- } |
|
| 106 |
- setupDockerSecrets(pod, build.Spec.Output.PushSecret, strategy.PullSecret, sourceImageSecret) |
|
| 102 |
+ setupDockerSecrets(pod, build.Spec.Output.PushSecret, strategy.PullSecret, build.Spec.Source.Images) |
|
| 107 | 103 |
setupSourceSecrets(pod, build.Spec.Source.SourceSecret) |
| 108 | 104 |
setupSecrets(pod, build.Spec.Source.Secrets) |
| 109 | 105 |
return pod, nil |
| ... | ... |
@@ -1,10 +1,13 @@ |
| 1 | 1 |
package strategy |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "fmt" |
|
| 4 | 5 |
"path/filepath" |
| 6 |
+ "strconv" |
|
| 5 | 7 |
|
| 6 | 8 |
"github.com/golang/glog" |
| 7 | 9 |
buildapi "github.com/openshift/origin/pkg/build/api" |
| 10 |
+ "github.com/openshift/origin/pkg/build/builder/cmd/dockercfg" |
|
| 8 | 11 |
imageapi "github.com/openshift/origin/pkg/image/api" |
| 9 | 12 |
"github.com/openshift/origin/pkg/util/namer" |
| 10 | 13 |
kapi "k8s.io/kubernetes/pkg/api" |
| ... | ... |
@@ -78,8 +81,8 @@ func setupBuildEnv(build *buildapi.Build, pod *kapi.Pod) error {
|
| 78 | 78 |
|
| 79 | 79 |
// mountSecretVolume is a helper method responsible for actual mounting secret |
| 80 | 80 |
// volumes into a pod. |
| 81 |
-func mountSecretVolume(pod *kapi.Pod, secretName, mountPath, volumePrefix string) {
|
|
| 82 |
- volumeName := namer.GetName(secretName, volumePrefix, kvalidation.DNS1123SubdomainMaxLength) |
|
| 81 |
+func mountSecretVolume(pod *kapi.Pod, secretName, mountPath, volumeSuffix string) {
|
|
| 82 |
+ volumeName := namer.GetName(secretName, volumeSuffix, kvalidation.DNS1123SubdomainMaxLength) |
|
| 83 | 83 |
volume := kapi.Volume{
|
| 84 | 84 |
Name: volumeName, |
| 85 | 85 |
VolumeSource: kapi.VolumeSource{
|
| ... | ... |
@@ -99,7 +102,7 @@ func mountSecretVolume(pod *kapi.Pod, secretName, mountPath, volumePrefix string |
| 99 | 99 |
|
| 100 | 100 |
// setupDockerSecrets mounts Docker Registry secrets into Pod running the build, |
| 101 | 101 |
// allowing Docker to authenticate against private registries or Docker Hub. |
| 102 |
-func setupDockerSecrets(pod *kapi.Pod, pushSecret, pullSecret, sourceImageSecret *kapi.LocalObjectReference) {
|
|
| 102 |
+func setupDockerSecrets(pod *kapi.Pod, pushSecret, pullSecret *kapi.LocalObjectReference, imageSources []buildapi.ImageSource) {
|
|
| 103 | 103 |
if pushSecret != nil {
|
| 104 | 104 |
mountSecretVolume(pod, pushSecret.Name, DockerPushSecretMountPath, "push") |
| 105 | 105 |
pod.Spec.Containers[0].Env = append(pod.Spec.Containers[0].Env, []kapi.EnvVar{
|
| ... | ... |
@@ -116,12 +119,16 @@ func setupDockerSecrets(pod *kapi.Pod, pushSecret, pullSecret, sourceImageSecret |
| 116 | 116 |
glog.V(3).Infof("%s will be used for docker pull in %s", DockerPullSecretMountPath, pod.Name)
|
| 117 | 117 |
} |
| 118 | 118 |
|
| 119 |
- if sourceImageSecret != nil {
|
|
| 120 |
- mountSecretVolume(pod, sourceImageSecret.Name, SourceImagePullSecretMountPath, "source-image") |
|
| 119 |
+ for i, imageSource := range imageSources {
|
|
| 120 |
+ if imageSource.PullSecret == nil {
|
|
| 121 |
+ continue |
|
| 122 |
+ } |
|
| 123 |
+ mountPath := filepath.Join(SourceImagePullSecretMountPath, strconv.Itoa(i)) |
|
| 124 |
+ mountSecretVolume(pod, imageSource.PullSecret.Name, mountPath, "source-image") |
|
| 121 | 125 |
pod.Spec.Containers[0].Env = append(pod.Spec.Containers[0].Env, []kapi.EnvVar{
|
| 122 |
- {Name: "PULL_SOURCE_DOCKERCFG_PATH", Value: filepath.Join(SourceImagePullSecretMountPath, kapi.DockerConfigKey)},
|
|
| 126 |
+ {Name: fmt.Sprintf("%s%d", dockercfg.PullSourceAuthType, i), Value: filepath.Join(mountPath, kapi.DockerConfigKey)},
|
|
| 123 | 127 |
}...) |
| 124 |
- glog.V(3).Infof("%s will be used for docker pull in %s", SourceImagePullSecretMountPath, pod.Name)
|
|
| 128 |
+ glog.V(3).Infof("%s will be used for docker pull in %s", mountPath, pod.Name)
|
|
| 125 | 129 |
|
| 126 | 130 |
} |
| 127 | 131 |
} |
| ... | ... |
@@ -424,16 +424,18 @@ func (g *BuildGenerator) generateBuildFromConfig(ctx kapi.Context, bc *buildapi. |
| 424 | 424 |
strategyImageChangeTrigger := getStrategyImageChangeTrigger(bc) |
| 425 | 425 |
|
| 426 | 426 |
// Resolve image source if present |
| 427 |
- if build.Spec.Source.Image != nil {
|
|
| 428 |
- if build.Spec.Source.Image.PullSecret == nil {
|
|
| 429 |
- build.Spec.Source.Image.PullSecret = g.resolveImageSecret(ctx, builderSecrets, &build.Spec.Source.Image.From, bc.Namespace) |
|
| 427 |
+ for i, sourceImage := range build.Spec.Source.Images {
|
|
| 428 |
+ if sourceImage.PullSecret == nil {
|
|
| 429 |
+ sourceImage.PullSecret = g.resolveImageSecret(ctx, builderSecrets, &sourceImage.From, bc.Namespace) |
|
| 430 | 430 |
} |
| 431 |
- sourceImage, err := g.resolveImageStreamReference(ctx, build.Spec.Source.Image.From, bc.Namespace) |
|
| 431 |
+ sourceImageSpec, err := g.resolveImageStreamReference(ctx, sourceImage.From, bc.Namespace) |
|
| 432 | 432 |
if err != nil {
|
| 433 | 433 |
return nil, err |
| 434 | 434 |
} |
| 435 |
- build.Spec.Source.Image.From.Kind = "DockerImage" |
|
| 436 |
- build.Spec.Source.Image.From.Name = sourceImage |
|
| 435 |
+ sourceImage.From.Kind = "DockerImage" |
|
| 436 |
+ sourceImage.From.Name = sourceImageSpec |
|
| 437 |
+ sourceImage.From.Namespace = "" |
|
| 438 |
+ build.Spec.Source.Images[i] = sourceImage |
|
| 437 | 439 |
} |
| 438 | 440 |
|
| 439 | 441 |
// If the Build is using a From reference instead of a resolved image, we need to resolve that From |
| ... | ... |
@@ -456,6 +456,13 @@ func setupAppConfig(f *clientcmd.Factory, out io.Writer, c *cobra.Command, args |
| 456 | 456 |
if config.AllowMissingImages && config.AsSearch {
|
| 457 | 457 |
return cmdutil.UsageError(c, "--allow-missing-images and --search are mutually exclusive.") |
| 458 | 458 |
} |
| 459 |
+ |
|
| 460 |
+ if len(config.SourceImage) != 0 && len(config.SourceImagePath) == 0 {
|
|
| 461 |
+ return cmdutil.UsageError(c, "--source-image-path must be specified when --source-image is specified.") |
|
| 462 |
+ } |
|
| 463 |
+ if len(config.SourceImage) == 0 && len(config.SourceImagePath) != 0 {
|
|
| 464 |
+ return cmdutil.UsageError(c, "--source-image must be specified when --source-image-path is specified.") |
|
| 465 |
+ } |
|
| 459 | 466 |
return nil |
| 460 | 467 |
} |
| 461 | 468 |
|
| ... | ... |
@@ -51,7 +51,10 @@ You can use '%[1]s status' to check the progress.` |
| 51 | 51 |
$ %[1]s new-build https://github.com/openshift/ruby-hello-world RACK_ENV=development |
| 52 | 52 |
|
| 53 | 53 |
# Create a build config from a remote repository and inject the npmrc into a build |
| 54 |
- $ %[1]s new-build https://github.com/openshift/ruby-hello-world --build-secret npmrc:.npmrc` |
|
| 54 |
+ $ %[1]s new-build https://github.com/openshift/ruby-hello-world --build-secret npmrc:.npmrc |
|
| 55 |
+ |
|
| 56 |
+ # Create a build config that gets its input from a remote repository and another Docker image |
|
| 57 |
+ $ %[1]s new-build https://github.com/openshift/ruby-hello-world --image-source=openshift/jenkins-1-centos7 --image-source-path=/var/lib/jenkins:/tmp` |
|
| 55 | 58 |
|
| 56 | 59 |
newBuildNoInput = `You must specify one or more images, image streams, or source code locations to create a build. |
| 57 | 60 |
|
| ... | ... |
@@ -113,6 +116,8 @@ func NewCmdNewBuild(fullName string, f *clientcmd.Factory, in io.Reader, out io. |
| 113 | 113 |
cmd.Flags().StringVar(&config.ContextDir, "context-dir", "", "Context directory to be used for the build.") |
| 114 | 114 |
cmd.Flags().BoolVar(&config.DryRun, "dry-run", false, "If true, do not actually create resources.") |
| 115 | 115 |
cmd.Flags().BoolVar(&config.NoOutput, "no-output", false, "If true, the build output will not be pushed anywhere.") |
| 116 |
+ cmd.Flags().StringVar(&config.SourceImage, "source-image", "", "Specify an image to use as source for the build. You must also specify --source-image-path.") |
|
| 117 |
+ cmd.Flags().StringVar(&config.SourceImagePath, "source-image-path", "", "Specify the file or directory to copy from the source image and its destination in the build directory. Format: [source]:[destination-dir].") |
|
| 116 | 118 |
cmdutil.AddPrinterFlags(cmd) |
| 117 | 119 |
|
| 118 | 120 |
return cmd |
| ... | ... |
@@ -173,6 +173,12 @@ type BuildConfigDescriber struct {
|
| 173 | 173 |
host string |
| 174 | 174 |
} |
| 175 | 175 |
|
| 176 |
+func nameAndNamespace(ns, name string) string {
|
|
| 177 |
+ if len(ns) != 0 {
|
|
| 178 |
+ return fmt.Sprintf("%s/%s", ns, name)
|
|
| 179 |
+ } |
|
| 180 |
+ return name |
|
| 181 |
+} |
|
| 176 | 182 |
func describeBuildSpec(p buildapi.BuildSpec, out *tabwriter.Writer) {
|
| 177 | 183 |
formatString(out, "Strategy", buildapi.StrategyType(p.Strategy)) |
| 178 | 184 |
if p.Source.Dockerfile != nil {
|
| ... | ... |
@@ -223,7 +229,18 @@ func describeBuildSpec(p buildapi.BuildSpec, out *tabwriter.Writer) {
|
| 223 | 223 |
} |
| 224 | 224 |
formatString(out, "Build Secrets", strings.Join(result, ",")) |
| 225 | 225 |
} |
| 226 |
- |
|
| 226 |
+ if len(p.Source.Images) == 1 && len(p.Source.Images[0].Paths) == 1 {
|
|
| 227 |
+ image := p.Source.Images[0] |
|
| 228 |
+ path := image.Paths[0] |
|
| 229 |
+ formatString(out, "Image Source", fmt.Sprintf("copies %s from %s to %s", path.SourcePath, nameAndNamespace(image.From.Namespace, image.From.Name), path.DestinationDir))
|
|
| 230 |
+ } else {
|
|
| 231 |
+ for _, image := range p.Source.Images {
|
|
| 232 |
+ formatString(out, "Image Source", fmt.Sprintf("%s", nameAndNamespace(image.From.Namespace, image.From.Name)))
|
|
| 233 |
+ for _, path := range image.Paths {
|
|
| 234 |
+ fmt.Fprintf(out, "\t- %s -> %s\n", path.SourcePath, path.DestinationDir) |
|
| 235 |
+ } |
|
| 236 |
+ } |
|
| 237 |
+ } |
|
| 227 | 238 |
switch {
|
| 228 | 239 |
case p.Strategy.DockerStrategy != nil: |
| 229 | 240 |
describeDockerStrategy(p.Strategy.DockerStrategy, out) |
| ... | ... |
@@ -234,11 +251,7 @@ func describeBuildSpec(p buildapi.BuildSpec, out *tabwriter.Writer) {
|
| 234 | 234 |
} |
| 235 | 235 |
|
| 236 | 236 |
if p.Output.To != nil {
|
| 237 |
- if len(p.Output.To.Namespace) != 0 {
|
|
| 238 |
- formatString(out, "Output to", fmt.Sprintf("%s %s/%s", p.Output.To.Kind, p.Output.To.Namespace, p.Output.To.Name))
|
|
| 239 |
- } else {
|
|
| 240 |
- formatString(out, "Output to", fmt.Sprintf("%s %s", p.Output.To.Kind, p.Output.To.Name))
|
|
| 241 |
- } |
|
| 237 |
+ formatString(out, "Output to", fmt.Sprintf("%s %s", p.Output.To.Kind, nameAndNamespace(p.Output.To.Namespace, p.Output.To.Name)))
|
|
| 242 | 238 |
} |
| 243 | 239 |
|
| 244 | 240 |
if p.Output.PushSecret != nil {
|
| ... | ... |
@@ -263,11 +276,7 @@ func describeBuildSpec(p buildapi.BuildSpec, out *tabwriter.Writer) {
|
| 263 | 263 |
|
| 264 | 264 |
func describeSourceStrategy(s *buildapi.SourceBuildStrategy, out *tabwriter.Writer) {
|
| 265 | 265 |
if len(s.From.Name) != 0 {
|
| 266 |
- if len(s.From.Namespace) != 0 {
|
|
| 267 |
- formatString(out, "From Image", fmt.Sprintf("%s %s/%s", s.From.Kind, s.From.Namespace, s.From.Name))
|
|
| 268 |
- } else {
|
|
| 269 |
- formatString(out, "From Image", fmt.Sprintf("%s %s", s.From.Kind, s.From.Name))
|
|
| 270 |
- } |
|
| 266 |
+ formatString(out, "From Image", fmt.Sprintf("%s %s", s.From.Kind, nameAndNamespace(s.From.Namespace, s.From.Name)))
|
|
| 271 | 267 |
} |
| 272 | 268 |
if len(s.Scripts) != 0 {
|
| 273 | 269 |
formatString(out, "Scripts", s.Scripts) |
| ... | ... |
@@ -285,11 +294,7 @@ func describeSourceStrategy(s *buildapi.SourceBuildStrategy, out *tabwriter.Writ |
| 285 | 285 |
|
| 286 | 286 |
func describeDockerStrategy(s *buildapi.DockerBuildStrategy, out *tabwriter.Writer) {
|
| 287 | 287 |
if s.From != nil && len(s.From.Name) != 0 {
|
| 288 |
- if len(s.From.Namespace) != 0 {
|
|
| 289 |
- formatString(out, "From Image", fmt.Sprintf("%s %s/%s", s.From.Kind, s.From.Namespace, s.From.Name))
|
|
| 290 |
- } else {
|
|
| 291 |
- formatString(out, "From Image", fmt.Sprintf("%s %s", s.From.Kind, s.From.Name))
|
|
| 292 |
- } |
|
| 288 |
+ formatString(out, "From Image", fmt.Sprintf("%s %s", s.From.Kind, nameAndNamespace(s.From.Namespace, s.From.Name)))
|
|
| 293 | 289 |
} |
| 294 | 290 |
if len(s.DockerfilePath) != 0 {
|
| 295 | 291 |
formatString(out, "Dockerfile Path", s.DockerfilePath) |
| ... | ... |
@@ -307,11 +312,7 @@ func describeDockerStrategy(s *buildapi.DockerBuildStrategy, out *tabwriter.Writ |
| 307 | 307 |
|
| 308 | 308 |
func describeCustomStrategy(s *buildapi.CustomBuildStrategy, out *tabwriter.Writer) {
|
| 309 | 309 |
if len(s.From.Name) != 0 {
|
| 310 |
- if len(s.From.Namespace) != 0 {
|
|
| 311 |
- formatString(out, "Image Reference", fmt.Sprintf("%s %s/%s", s.From.Kind, s.From.Namespace, s.From.Name))
|
|
| 312 |
- } else {
|
|
| 313 |
- formatString(out, "Image Reference", fmt.Sprintf("%s %s", s.From.Kind, s.From.Name))
|
|
| 314 |
- } |
|
| 310 |
+ formatString(out, "Image Reference", fmt.Sprintf("%s %s", s.From.Kind, nameAndNamespace(s.From.Namespace, s.From.Name)))
|
|
| 315 | 311 |
} |
| 316 | 312 |
if s.ExposeDockerSocket {
|
| 317 | 313 |
formatString(out, "Expose Docker Socket", "yes") |
| ... | ... |
@@ -100,6 +100,10 @@ type SourceRef struct {
|
| 100 | 100 |
ContextDir string |
| 101 | 101 |
Secrets []buildapi.SecretBuildSource |
| 102 | 102 |
|
| 103 |
+ SourceImage *ImageRef |
|
| 104 |
+ ImageSourcePath string |
|
| 105 |
+ ImageDestPath string |
|
| 106 |
+ |
|
| 103 | 107 |
DockerfileContents string |
| 104 | 108 |
|
| 105 | 109 |
Binary bool |
| ... | ... |
@@ -153,6 +157,24 @@ func (r *SourceRef) BuildSource() (*buildapi.BuildSource, []buildapi.BuildTrigge |
| 153 | 153 |
if r.Binary {
|
| 154 | 154 |
source.Binary = &buildapi.BinaryBuildSource{}
|
| 155 | 155 |
} |
| 156 |
+ if r.SourceImage != nil {
|
|
| 157 |
+ objRef := r.SourceImage.ObjectReference() |
|
| 158 |
+ imgSrc := buildapi.ImageSource{}
|
|
| 159 |
+ imgSrc.From = objRef |
|
| 160 |
+ imgSrc.Paths = []buildapi.ImageSourcePath{
|
|
| 161 |
+ {
|
|
| 162 |
+ SourcePath: r.ImageSourcePath, |
|
| 163 |
+ DestinationDir: r.ImageDestPath, |
|
| 164 |
+ }, |
|
| 165 |
+ } |
|
| 166 |
+ triggers = append(triggers, buildapi.BuildTriggerPolicy{
|
|
| 167 |
+ Type: buildapi.ImageChangeBuildTriggerType, |
|
| 168 |
+ ImageChange: &buildapi.ImageChangeTrigger{
|
|
| 169 |
+ From: &objRef, |
|
| 170 |
+ }, |
|
| 171 |
+ }) |
|
| 172 |
+ source.Images = []buildapi.ImageSource{imgSrc}
|
|
| 173 |
+ } |
|
| 156 | 174 |
return source, triggers |
| 157 | 175 |
} |
| 158 | 176 |
|
| ... | ... |
@@ -75,6 +75,9 @@ type AppConfig struct {
|
| 75 | 75 |
AllowMissingImages bool |
| 76 | 76 |
Deploy bool |
| 77 | 77 |
|
| 78 |
+ SourceImage string |
|
| 79 |
+ SourceImagePath string |
|
| 80 |
+ |
|
| 78 | 81 |
SkipGeneration bool |
| 79 | 82 |
AllowGenerationErrors bool |
| 80 | 83 |
|
| ... | ... |
@@ -491,6 +494,17 @@ func (c *AppConfig) search(components app.ComponentReferences) error {
|
| 491 | 491 |
return errors.NewAggregate(errs) |
| 492 | 492 |
} |
| 493 | 493 |
|
| 494 |
+func (c *AppConfig) detectPartialMatches(components app.ComponentReferences) error {
|
|
| 495 |
+ errs := []error{}
|
|
| 496 |
+ for _, ref := range components {
|
|
| 497 |
+ input := ref.Input() |
|
| 498 |
+ if input.ResolvedMatch.Score != 0.0 {
|
|
| 499 |
+ errs = append(errs, fmt.Errorf("component %q had only a partial match of %q - if this is the value you want to use, specify it explicitly", input.From, input.ResolvedMatch.Name))
|
|
| 500 |
+ } |
|
| 501 |
+ } |
|
| 502 |
+ return errors.NewAggregate(errs) |
|
| 503 |
+} |
|
| 504 |
+ |
|
| 494 | 505 |
// inferBuildTypes infers build status and mismatches between source and docker builders |
| 495 | 506 |
func (c *AppConfig) inferBuildTypes(components app.ComponentReferences) (app.ComponentReferences, error) {
|
| 496 | 507 |
errs := []error{}
|
| ... | ... |
@@ -526,9 +540,6 @@ func (c *AppConfig) inferBuildTypes(components app.ComponentReferences) (app.Com |
| 526 | 526 |
errs = append(errs, fmt.Errorf("the resolved match %q for component %q cannot build source code - check whether this is the image you want to use, then use --strategy=source to build using source or --strategy=docker to treat this as a Docker base image and set up a layered Docker build", input.ResolvedMatch.Name, ref))
|
| 527 | 527 |
continue |
| 528 | 528 |
} |
| 529 |
- case input.ResolvedMatch.Score != 0.0: |
|
| 530 |
- errs = append(errs, fmt.Errorf("component %q had only a partial match of %q - if this is the value you want to use, specify it explicitly", input.From, input.ResolvedMatch.Name))
|
|
| 531 |
- continue |
|
| 532 | 529 |
} |
| 533 | 530 |
} |
| 534 | 531 |
if len(components) == 0 && c.BinaryBuild {
|
| ... | ... |
@@ -567,8 +578,9 @@ func (c *AppConfig) ensureHasSource(components app.ComponentReferences, reposito |
| 567 | 567 |
return fmt.Errorf("the following images require source code: %s\n"+
|
| 568 | 568 |
" and the following repositories are not used: %s\nUse '[image]~[repo]' to declare which code goes with which image", components, repositories) |
| 569 | 569 |
case len(repositories) == 1: |
| 570 |
- glog.Infof("Using %q as the source for build", repositories[0])
|
|
| 570 |
+ glog.V(2).Infof("Using %q as the source for build", repositories[0])
|
|
| 571 | 571 |
for _, component := range components {
|
| 572 |
+ glog.V(2).Infof("Pairing with component %v", component)
|
|
| 572 | 573 |
component.Input().Use(repositories[0]) |
| 573 | 574 |
repositories[0].UsedBy(component) |
| 574 | 575 |
} |
| ... | ... |
@@ -912,6 +924,47 @@ func (c *AppConfig) RunQuery() (*QueryResult, error) {
|
| 912 | 912 |
}, nil |
| 913 | 913 |
} |
| 914 | 914 |
|
| 915 |
+func (c *AppConfig) addImageSource(sourceRepos app.SourceRepositories) (app.ComponentReference, app.SourceRepositories, error) {
|
|
| 916 |
+ if len(c.SourceImage) == 0 {
|
|
| 917 |
+ return nil, sourceRepos, nil |
|
| 918 |
+ } |
|
| 919 |
+ paths := strings.SplitN(c.SourceImagePath, ":", 2) |
|
| 920 |
+ var sourcePath, destPath string |
|
| 921 |
+ switch len(paths) {
|
|
| 922 |
+ case 1: |
|
| 923 |
+ sourcePath = paths[0] |
|
| 924 |
+ case 2: |
|
| 925 |
+ sourcePath = paths[0] |
|
| 926 |
+ destPath = paths[1] |
|
| 927 |
+ } |
|
| 928 |
+ compRef, _, err := app.NewComponentInput(c.SourceImage) |
|
| 929 |
+ if err != nil {
|
|
| 930 |
+ return nil, nil, err |
|
| 931 |
+ } |
|
| 932 |
+ resolver := app.PerfectMatchWeightedResolver{}
|
|
| 933 |
+ if c.imageStreamByAnnotationSearcher != nil {
|
|
| 934 |
+ resolver = append(resolver, app.WeightedResolver{Searcher: c.imageStreamByAnnotationSearcher, Weight: 0.0})
|
|
| 935 |
+ } |
|
| 936 |
+ if c.imageStreamSearcher != nil {
|
|
| 937 |
+ resolver = append(resolver, app.WeightedResolver{Searcher: c.imageStreamSearcher, Weight: 1.0})
|
|
| 938 |
+ } |
|
| 939 |
+ if c.dockerSearcher != nil {
|
|
| 940 |
+ resolver = append(resolver, app.WeightedResolver{Searcher: c.dockerSearcher, Weight: 2.0})
|
|
| 941 |
+ } |
|
| 942 |
+ compRef.Resolver = resolver |
|
| 943 |
+ switch len(sourceRepos) {
|
|
| 944 |
+ case 0: |
|
| 945 |
+ sourceRepos = append(sourceRepos, app.NewImageSourceRepository(compRef, sourcePath, destPath)) |
|
| 946 |
+ case 1: |
|
| 947 |
+ sourceRepos[0].SetSourceImage(compRef) |
|
| 948 |
+ sourceRepos[0].SetSourceImagePath(sourcePath, destPath) |
|
| 949 |
+ default: |
|
| 950 |
+ return nil, nil, fmt.Errorf("--image-source cannot be used with multiple source repositories")
|
|
| 951 |
+ } |
|
| 952 |
+ |
|
| 953 |
+ return compRef, sourceRepos, nil |
|
| 954 |
+} |
|
| 955 |
+ |
|
| 915 | 956 |
// run executes the provided config applying provided acceptors. |
| 916 | 957 |
func (c *AppConfig) run(acceptors app.Acceptors) (*AppResult, error) {
|
| 917 | 958 |
c.ensureDockerSearcher() |
| ... | ... |
@@ -928,7 +981,21 @@ func (c *AppConfig) run(acceptors app.Acceptors) (*AppResult, error) {
|
| 928 | 928 |
return nil, err |
| 929 | 929 |
} |
| 930 | 930 |
|
| 931 |
- if err := c.resolve(components); err != nil {
|
|
| 931 |
+ var imageComp app.ComponentReference |
|
| 932 |
+ imageComp, repositories, err = c.addImageSource(repositories) |
|
| 933 |
+ if err != nil {
|
|
| 934 |
+ return nil, err |
|
| 935 |
+ } |
|
| 936 |
+ componentsIncludingImageComps := components |
|
| 937 |
+ if imageComp != nil {
|
|
| 938 |
+ componentsIncludingImageComps = append(components, imageComp) |
|
| 939 |
+ } |
|
| 940 |
+ if err := c.resolve(componentsIncludingImageComps); err != nil {
|
|
| 941 |
+ return nil, err |
|
| 942 |
+ } |
|
| 943 |
+ |
|
| 944 |
+ err = c.detectPartialMatches(componentsIncludingImageComps) |
|
| 945 |
+ if err != nil {
|
|
| 932 | 946 |
return nil, err |
| 933 | 947 |
} |
| 934 | 948 |
|
| ... | ... |
@@ -1002,6 +1002,7 @@ func TestRunBuilds(t *testing.T) {
|
| 1002 | 1002 |
checkResult func(*AppResult) error |
| 1003 | 1003 |
checkOutput func(stdout, stderr io.Reader) error |
| 1004 | 1004 |
}{
|
| 1005 |
+ |
|
| 1005 | 1006 |
{
|
| 1006 | 1007 |
name: "successful ruby app generation", |
| 1007 | 1008 |
config: &AppConfig{
|
| ... | ... |
@@ -1210,6 +1211,116 @@ func TestRunBuilds(t *testing.T) {
|
| 1210 | 1210 |
return err.Error() == "--dockerfile cannot be used with multiple source repositories" |
| 1211 | 1211 |
}, |
| 1212 | 1212 |
}, |
| 1213 |
+ |
|
| 1214 |
+ {
|
|
| 1215 |
+ name: "successful input image source build with a repository", |
|
| 1216 |
+ config: &AppConfig{
|
|
| 1217 |
+ SourceRepositories: []string{
|
|
| 1218 |
+ "https://github.com/openshift/ruby-hello-world", |
|
| 1219 |
+ }, |
|
| 1220 |
+ SourceImage: "centos/mongodb-26-centos7", |
|
| 1221 |
+ SourceImagePath: "/src:dst", |
|
| 1222 |
+ }, |
|
| 1223 |
+ expected: map[string][]string{
|
|
| 1224 |
+ "buildConfig": {"ruby-hello-world"},
|
|
| 1225 |
+ "imageStream": {"mongodb-26-centos7", "ruby-22-centos7", "ruby-hello-world"},
|
|
| 1226 |
+ }, |
|
| 1227 |
+ checkResult: func(res *AppResult) error {
|
|
| 1228 |
+ var bc *buildapi.BuildConfig |
|
| 1229 |
+ for _, item := range res.List.Items {
|
|
| 1230 |
+ switch v := item.(type) {
|
|
| 1231 |
+ case *buildapi.BuildConfig: |
|
| 1232 |
+ if bc != nil {
|
|
| 1233 |
+ return fmt.Errorf("want one BuildConfig got multiple: %#v", res.List.Items)
|
|
| 1234 |
+ } |
|
| 1235 |
+ bc = v |
|
| 1236 |
+ } |
|
| 1237 |
+ } |
|
| 1238 |
+ if bc == nil {
|
|
| 1239 |
+ return fmt.Errorf("want one BuildConfig got none: %#v", res.List.Items)
|
|
| 1240 |
+ } |
|
| 1241 |
+ var got string |
|
| 1242 |
+ |
|
| 1243 |
+ want := "mongodb-26-centos7:latest" |
|
| 1244 |
+ got = bc.Spec.Source.Images[0].From.Name |
|
| 1245 |
+ if got != want {
|
|
| 1246 |
+ return fmt.Errorf("bc.Spec.Source.Image.From.Name = %q; want %q", got, want)
|
|
| 1247 |
+ } |
|
| 1248 |
+ |
|
| 1249 |
+ want = "ImageStreamTag" |
|
| 1250 |
+ got = bc.Spec.Source.Images[0].From.Kind |
|
| 1251 |
+ if got != want {
|
|
| 1252 |
+ return fmt.Errorf("bc.Spec.Source.Image.From.Kind = %q; want %q", got, want)
|
|
| 1253 |
+ } |
|
| 1254 |
+ |
|
| 1255 |
+ want = "/src" |
|
| 1256 |
+ got = bc.Spec.Source.Images[0].Paths[0].SourcePath |
|
| 1257 |
+ if got != want {
|
|
| 1258 |
+ return fmt.Errorf("bc.Spec.Source.Image.Paths[0].SourcePath = %q; want %q", got, want)
|
|
| 1259 |
+ } |
|
| 1260 |
+ |
|
| 1261 |
+ want = "dst" |
|
| 1262 |
+ got = bc.Spec.Source.Images[0].Paths[0].DestinationDir |
|
| 1263 |
+ if got != want {
|
|
| 1264 |
+ return fmt.Errorf("bc.Spec.Source.Image.Paths[0].DestinationDir = %q; want %q", got, want)
|
|
| 1265 |
+ } |
|
| 1266 |
+ return nil |
|
| 1267 |
+ }, |
|
| 1268 |
+ }, |
|
| 1269 |
+ {
|
|
| 1270 |
+ name: "successful input image source build with no repository", |
|
| 1271 |
+ config: &AppConfig{
|
|
| 1272 |
+ Components: []string{"centos/mysql-56-centos7"},
|
|
| 1273 |
+ To: "outputimage", |
|
| 1274 |
+ SourceImage: "centos/mongodb-26-centos7", |
|
| 1275 |
+ SourceImagePath: "/src:dst", |
|
| 1276 |
+ }, |
|
| 1277 |
+ expected: map[string][]string{
|
|
| 1278 |
+ "buildConfig": {"outputimage"},
|
|
| 1279 |
+ "imageStream": {"mongodb-26-centos7", "mysql-56-centos7", "outputimage"},
|
|
| 1280 |
+ }, |
|
| 1281 |
+ checkResult: func(res *AppResult) error {
|
|
| 1282 |
+ var bc *buildapi.BuildConfig |
|
| 1283 |
+ for _, item := range res.List.Items {
|
|
| 1284 |
+ switch v := item.(type) {
|
|
| 1285 |
+ case *buildapi.BuildConfig: |
|
| 1286 |
+ if bc != nil {
|
|
| 1287 |
+ return fmt.Errorf("want one BuildConfig got multiple: %#v", res.List.Items)
|
|
| 1288 |
+ } |
|
| 1289 |
+ bc = v |
|
| 1290 |
+ } |
|
| 1291 |
+ } |
|
| 1292 |
+ if bc == nil {
|
|
| 1293 |
+ return fmt.Errorf("want one BuildConfig got none: %#v", res.List.Items)
|
|
| 1294 |
+ } |
|
| 1295 |
+ var got string |
|
| 1296 |
+ |
|
| 1297 |
+ want := "mongodb-26-centos7:latest" |
|
| 1298 |
+ got = bc.Spec.Source.Images[0].From.Name |
|
| 1299 |
+ if got != want {
|
|
| 1300 |
+ return fmt.Errorf("bc.Spec.Source.Image.From.Name = %q; want %q", got, want)
|
|
| 1301 |
+ } |
|
| 1302 |
+ |
|
| 1303 |
+ want = "ImageStreamTag" |
|
| 1304 |
+ got = bc.Spec.Source.Images[0].From.Kind |
|
| 1305 |
+ if got != want {
|
|
| 1306 |
+ return fmt.Errorf("bc.Spec.Source.Image.From.Kind = %q; want %q", got, want)
|
|
| 1307 |
+ } |
|
| 1308 |
+ |
|
| 1309 |
+ want = "/src" |
|
| 1310 |
+ got = bc.Spec.Source.Images[0].Paths[0].SourcePath |
|
| 1311 |
+ if got != want {
|
|
| 1312 |
+ return fmt.Errorf("bc.Spec.Source.Image.Paths[0].SourcePath = %q; want %q", got, want)
|
|
| 1313 |
+ } |
|
| 1314 |
+ |
|
| 1315 |
+ want = "dst" |
|
| 1316 |
+ got = bc.Spec.Source.Images[0].Paths[0].DestinationDir |
|
| 1317 |
+ if got != want {
|
|
| 1318 |
+ return fmt.Errorf("bc.Spec.Source.Image.Paths[0].DestinationDir = %q; want %q", got, want)
|
|
| 1319 |
+ } |
|
| 1320 |
+ return nil |
|
| 1321 |
+ }, |
|
| 1322 |
+ }, |
|
| 1213 | 1323 |
} |
| 1214 | 1324 |
for _, test := range tests {
|
| 1215 | 1325 |
stdout, stderr := PrepareAppConfig(test.config) |
| ... | ... |
@@ -1280,11 +1391,7 @@ func PrepareAppConfig(config *AppConfig) (stdout, stderr *bytes.Buffer) {
|
| 1280 | 1280 |
config.dockerSearcher = app.DockerRegistrySearcher{
|
| 1281 | 1281 |
Client: dockerregistry.NewClient(10 * time.Second), |
| 1282 | 1282 |
} |
| 1283 |
- config.imageStreamByAnnotationSearcher = &app.ImageStreamByAnnotationSearcher{
|
|
| 1284 |
- Client: &client.Fake{},
|
|
| 1285 |
- ImageStreamImages: &client.Fake{},
|
|
| 1286 |
- Namespaces: []string{"default"},
|
|
| 1287 |
- } |
|
| 1283 |
+ config.imageStreamByAnnotationSearcher = fakeImageStreamSearcher() |
|
| 1288 | 1284 |
config.imageStreamSearcher = fakeImageStreamSearcher() |
| 1289 | 1285 |
config.originNamespace = "default" |
| 1290 | 1286 |
config.osclient = &client.Fake{}
|
| ... | ... |
@@ -41,6 +41,7 @@ func (r PerfectMatchWeightedResolver) Resolve(value string) (*ComponentMatch, er |
| 41 | 41 |
imperfect := ScoredComponentMatches{}
|
| 42 | 42 |
var group WeightedResolvers |
| 43 | 43 |
for i, resolver := range r {
|
| 44 |
+ // lump all resolvers with the same weight into a single group |
|
| 44 | 45 |
if len(group) == 0 || resolver.Weight == group[0].Weight {
|
| 45 | 46 |
group = append(group, resolver) |
| 46 | 47 |
if i != len(r)-1 && r[i+1].Weight == group[0].Weight {
|
| ... | ... |
@@ -308,7 +309,10 @@ func resolveExact(resolver Resolver, value string) (exact *ComponentMatch, inexa |
| 308 | 308 |
return nil, nil, err |
| 309 | 309 |
} |
| 310 | 310 |
} |
| 311 |
- return match, nil, nil |
|
| 311 |
+ if match.Score == 0.0 {
|
|
| 312 |
+ return match, nil, nil |
|
| 313 |
+ } |
|
| 314 |
+ return nil, ComponentMatches{match}, nil
|
|
| 312 | 315 |
} |
| 313 | 316 |
|
| 314 | 317 |
func searchExact(searcher Searcher, value string) (exact *ComponentMatch, inexact []*ComponentMatch, err error) {
|
| ... | ... |
@@ -319,7 +323,6 @@ func searchExact(searcher Searcher, value string) (exact *ComponentMatch, inexac |
| 319 | 319 |
|
| 320 | 320 |
exactMatches := matches.Exact() |
| 321 | 321 |
inexactMatches := matches.Inexact() |
| 322 |
- |
|
| 323 | 322 |
switch len(exactMatches) {
|
| 324 | 323 |
case 0: |
| 325 | 324 |
return nil, inexactMatches, nil |
| ... | ... |
@@ -84,7 +84,7 @@ func (r DockerClientSearcher) Search(terms ...string) (ComponentMatches, error) |
| 84 | 84 |
if tags := matchTag(image, term, ref.Registry, ref.Namespace, ref.Name, ref.Tag); len(tags) > 0 {
|
| 85 | 85 |
for i := range tags {
|
| 86 | 86 |
tags[i].LocalOnly = true |
| 87 |
- glog.V(5).Infof("Found local docker image match %q for %q", tags[i].Value, term)
|
|
| 87 |
+ glog.V(5).Infof("Found local docker image match %q with score %f", tags[i].Value, tags[i].Score)
|
|
| 88 | 88 |
} |
| 89 | 89 |
termMatches = append(termMatches, tags...) |
| 90 | 90 |
} |
| ... | ... |
@@ -129,7 +129,6 @@ func (r DockerClientSearcher) Search(terms ...string) (ComponentMatches, error) |
| 129 | 129 |
if len(errs) != 0 {
|
| 130 | 130 |
return nil, utilerrors.NewAggregate(errs) |
| 131 | 131 |
} |
| 132 |
- |
|
| 133 | 132 |
return componentMatches, nil |
| 134 | 133 |
} |
| 135 | 134 |
|
| ... | ... |
@@ -241,6 +241,15 @@ func (p *Pipeline) Objects(accept, objectAccept Acceptor) (Objects, error) {
|
| 241 | 241 |
if objectAccept.Accept(build) {
|
| 242 | 242 |
objects = append(objects, build) |
| 243 | 243 |
} |
| 244 |
+ if p.Build.Source != nil && p.Build.Source.SourceImage != nil && p.Build.Source.SourceImage.AsImageStream && accept.Accept(p.Build.Source.SourceImage) {
|
|
| 245 |
+ srcImage, err := p.Build.Source.SourceImage.ImageStream() |
|
| 246 |
+ if err != nil {
|
|
| 247 |
+ return nil, err |
|
| 248 |
+ } |
|
| 249 |
+ if objectAccept.Accept(srcImage) {
|
|
| 250 |
+ objects = append(objects, srcImage) |
|
| 251 |
+ } |
|
| 252 |
+ } |
|
| 244 | 253 |
} |
| 245 | 254 |
if p.Deployment != nil && accept.Accept(p.Deployment) {
|
| 246 | 255 |
dc, err := p.Deployment.DeploymentConfig() |
| ... | ... |
@@ -82,13 +82,16 @@ func IsRemoteRepository(s string) bool {
|
| 82 | 82 |
|
| 83 | 83 |
// SourceRepository represents a code repository that may be the target of a build. |
| 84 | 84 |
type SourceRepository struct {
|
| 85 |
- location string |
|
| 86 |
- url url.URL |
|
| 87 |
- localDir string |
|
| 88 |
- remoteURL *url.URL |
|
| 89 |
- contextDir string |
|
| 90 |
- secrets []buildapi.SecretBuildSource |
|
| 91 |
- info *SourceRepositoryInfo |
|
| 85 |
+ location string |
|
| 86 |
+ url url.URL |
|
| 87 |
+ localDir string |
|
| 88 |
+ remoteURL *url.URL |
|
| 89 |
+ contextDir string |
|
| 90 |
+ secrets []buildapi.SecretBuildSource |
|
| 91 |
+ info *SourceRepositoryInfo |
|
| 92 |
+ sourceImage ComponentReference |
|
| 93 |
+ sourceImageFrom string |
|
| 94 |
+ sourceImageTo string |
|
| 92 | 95 |
|
| 93 | 96 |
usedBy []ComponentReference |
| 94 | 97 |
buildWithDocker bool |
| ... | ... |
@@ -131,6 +134,16 @@ func NewBinarySourceRepository() *SourceRepository {
|
| 131 | 131 |
} |
| 132 | 132 |
} |
| 133 | 133 |
|
| 134 |
+func NewImageSourceRepository(compRef ComponentReference, from, to string) *SourceRepository {
|
|
| 135 |
+ return &SourceRepository{
|
|
| 136 |
+ sourceImage: compRef, |
|
| 137 |
+ sourceImageFrom: from, |
|
| 138 |
+ sourceImageTo: to, |
|
| 139 |
+ ignoreRepository: true, |
|
| 140 |
+ location: compRef.Input().From, |
|
| 141 |
+ } |
|
| 142 |
+} |
|
| 143 |
+ |
|
| 134 | 144 |
// UsedBy sets up which component uses the source repository |
| 135 | 145 |
func (r *SourceRepository) UsedBy(ref ComponentReference) {
|
| 136 | 146 |
r.usedBy = append(r.usedBy, ref) |
| ... | ... |
@@ -257,6 +270,17 @@ func (r *SourceRepository) Secrets() []buildapi.SecretBuildSource {
|
| 257 | 257 |
return r.secrets |
| 258 | 258 |
} |
| 259 | 259 |
|
| 260 |
+// SetSourceImage sets the source(input) image for a repository |
|
| 261 |
+func (r *SourceRepository) SetSourceImage(c ComponentReference) {
|
|
| 262 |
+ r.sourceImage = c |
|
| 263 |
+} |
|
| 264 |
+ |
|
| 265 |
+// SetSourceImagePath sets the source/destination to use when copying from the SourceImage |
|
| 266 |
+func (r *SourceRepository) SetSourceImagePath(source, dest string) {
|
|
| 267 |
+ r.sourceImageFrom = source |
|
| 268 |
+ r.sourceImageTo = dest |
|
| 269 |
+} |
|
| 270 |
+ |
|
| 260 | 271 |
// AddDockerfile adds the Dockerfile contents to the SourceRepository and |
| 261 | 272 |
// configure it to build with Docker strategy. Returns an error if the contents |
| 262 | 273 |
// are invalid. |
| ... | ... |
@@ -422,6 +446,16 @@ func StrategyAndSourceForRepository(repo *SourceRepository, image *ImageRef) (*B |
| 422 | 422 |
Secrets: repo.secrets, |
| 423 | 423 |
} |
| 424 | 424 |
|
| 425 |
+ if repo.sourceImage != nil {
|
|
| 426 |
+ srcImageRef, err := InputImageFromMatch(repo.sourceImage.Input().ResolvedMatch) |
|
| 427 |
+ if err != nil {
|
|
| 428 |
+ return nil, nil, err |
|
| 429 |
+ } |
|
| 430 |
+ source.SourceImage = srcImageRef |
|
| 431 |
+ source.ImageSourcePath = repo.sourceImageFrom |
|
| 432 |
+ source.ImageDestPath = repo.sourceImageTo |
|
| 433 |
+ } |
|
| 434 |
+ |
|
| 425 | 435 |
if (repo.ignoreRepository || repo.forceAddDockerfile) && repo.Info() != nil && repo.Info().Dockerfile != nil {
|
| 426 | 436 |
source.DockerfileContents = repo.Info().Dockerfile.Contents() |
| 427 | 437 |
} |
| ... | ... |
@@ -132,6 +132,9 @@ os::cmd::expect_failure_and_text 'oc new-build mysql -o yaml' 'you must specify |
| 132 | 132 |
os::cmd::expect_success_and_text 'oc new-build mysql --binary -o yaml --to mysql:bin' 'type: Binary' |
| 133 | 133 |
os::cmd::expect_success_and_text 'oc new-build mysql https://github.com/openshift/ruby-hello-world --strategy=docker -o yaml' 'type: Docker' |
| 134 | 134 |
os::cmd::expect_failure_and_text 'oc new-build mysql https://github.com/openshift/ruby-hello-world --binary' 'specifying binary builds and source repositories at the same time is not allowed' |
| 135 |
+# new-build image source tests |
|
| 136 |
+os::cmd::expect_failure_and_text 'oc new-build mysql --source-image centos' 'error: --source-image-path must be specified when --source-image is specified.' |
|
| 137 |
+os::cmd::expect_failure_and_text 'oc new-build mysql --source-image-path foo' 'error: --source-image must be specified when --source-image-path is specified.' |
|
| 135 | 138 |
|
| 136 | 139 |
# do not allow use of non-existent image (should fail) |
| 137 | 140 |
os::cmd::expect_failure_and_text 'oc new-app openshift/bogusImage https://github.com/openshift/ruby-hello-world.git -o yaml' "no match for" |
| ... | ... |
@@ -1,42 +1,83 @@ |
| 1 | 1 |
package builds |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "fmt" |
|
| 4 |
+ "time" |
|
| 5 | 5 |
|
| 6 | 6 |
g "github.com/onsi/ginkgo" |
| 7 | 7 |
o "github.com/onsi/gomega" |
| 8 | 8 |
|
| 9 |
- "github.com/openshift/origin/test/extended/images" |
|
| 10 | 9 |
exutil "github.com/openshift/origin/test/extended/util" |
| 11 | 10 |
) |
| 12 | 11 |
|
| 13 | 12 |
var _ = g.Describe("builds: image source", func() {
|
| 14 | 13 |
defer g.GinkgoRecover() |
| 15 | 14 |
var ( |
| 16 |
- buildFixture = exutil.FixturePath("fixtures", "test-build-hello-openshift.yaml")
|
|
| 17 |
- helloBuilder = exutil.FixturePath("fixtures", "hello-builder")
|
|
| 18 |
- oc = exutil.NewCLI("build-image-source", exutil.KubeConfigPath())
|
|
| 15 |
+ buildFixture = exutil.FixturePath("fixtures", "test-imagesource-build.yaml")
|
|
| 16 |
+ oc = exutil.NewCLI("build-image-source", exutil.KubeConfigPath())
|
|
| 17 |
+ imageSourceLabel = exutil.ParseLabelsOrDie("app=imagesourceapp")
|
|
| 18 |
+ imageDockerLabel = exutil.ParseLabelsOrDie("app=imagedockerapp")
|
|
| 19 | 19 |
) |
| 20 | 20 |
|
| 21 | 21 |
g.JustBeforeEach(func() {
|
| 22 | 22 |
g.By("waiting for builder service account")
|
| 23 | 23 |
err := exutil.WaitForBuilderAccount(oc.KubeREST().ServiceAccounts(oc.Namespace())) |
| 24 | 24 |
o.Expect(err).NotTo(o.HaveOccurred()) |
| 25 |
+ |
|
| 26 |
+ g.By("waiting for imagestreams to be imported")
|
|
| 27 |
+ err = exutil.WaitForAnImageStream(oc.AdminREST().ImageStreams("openshift"), "jenkins", exutil.CheckImageStreamLatestTagPopulatedFn, exutil.CheckImageStreamTagNotFoundFn)
|
|
| 28 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 25 | 29 |
}) |
| 26 | 30 |
|
| 27 | 31 |
g.Describe("build with image source", func() {
|
| 28 |
- g.It("should complete successfully and deploy resulting image", func() {
|
|
| 29 |
- g.By("Creating build configs, deployment config, and service for hello-openshift")
|
|
| 32 |
+ g.It("should complete successfully and contain the expected file", func() {
|
|
| 33 |
+ g.By("Creating build configs for source build")
|
|
| 30 | 34 |
err := oc.Run("create").Args("-f", buildFixture).Execute()
|
| 31 | 35 |
o.Expect(err).NotTo(o.HaveOccurred()) |
| 32 |
- g.By("starting the builder image build with a directory")
|
|
| 33 |
- err = oc.Run("start-build").Args("hello-builder", fmt.Sprintf("--from-dir=%s", helloBuilder)).Execute()
|
|
| 36 |
+ g.By("starting the source strategy build")
|
|
| 37 |
+ err = oc.Run("start-build").Args("imagesourcebuild").Execute()
|
|
| 38 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 39 |
+ g.By("expecting the builds to complete successfully")
|
|
| 40 |
+ err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), "imagesourcebuild-1", exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn) |
|
| 41 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 42 |
+ |
|
| 43 |
+ g.By("expecting the pod to deploy successfully")
|
|
| 44 |
+ pods, err := exutil.WaitForPods(oc.KubeREST().Pods(oc.Namespace()), imageSourceLabel, exutil.CheckPodIsRunningFn, 1, 2*time.Minute) |
|
| 45 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 46 |
+ o.Expect(len(pods)).To(o.Equal(1)) |
|
| 47 |
+ pod, err := oc.KubeREST().Pods(oc.Namespace()).Get(pods[0]) |
|
| 34 | 48 |
o.Expect(err).NotTo(o.HaveOccurred()) |
| 35 |
- g.By("expect the builds to complete successfully and deploy a hello-openshift pod")
|
|
| 36 |
- success, err := images.CheckPageContains(oc, "hello-openshift", "", "Hello OpenShift!") |
|
| 49 |
+ |
|
| 50 |
+ g.By("expecting the pod to contain the file from the input image")
|
|
| 51 |
+ out, err := oc.Run("exec").Args(pod.Name, "-c", pod.Spec.Containers[0].Name, "--", "ls", "injected/dir").Output()
|
|
| 37 | 52 |
o.Expect(err).NotTo(o.HaveOccurred()) |
| 38 |
- o.Expect(success).To(o.BeTrue()) |
|
| 53 |
+ o.Expect(out).To(o.ContainSubstring("jenkins.war"))
|
|
| 54 |
+ }) |
|
| 55 |
+ }) |
|
| 56 |
+ g.Describe("build with image docker", func() {
|
|
| 57 |
+ g.It("should complete successfully and contain the expected file", func() {
|
|
| 58 |
+ g.By("Creating build configs for docker build")
|
|
| 59 |
+ err := oc.Run("create").Args("-f", buildFixture).Execute()
|
|
| 60 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 61 |
+ g.By("starting the docker strategy build")
|
|
| 62 |
+ err = oc.Run("start-build").Args("imagedockerbuild").Execute()
|
|
| 63 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 64 |
+ g.By("expect the builds to complete successfully")
|
|
| 65 |
+ err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), "imagedockerbuild-1", exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn) |
|
| 66 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 67 |
+ |
|
| 68 |
+ g.By("expect the pod to deploy successfully")
|
|
| 69 |
+ pods, err := exutil.WaitForPods(oc.KubeREST().Pods(oc.Namespace()), imageDockerLabel, exutil.CheckPodIsRunningFn, 1, 2*time.Minute) |
|
| 70 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 71 |
+ o.Expect(len(pods)).To(o.Equal(1)) |
|
| 72 |
+ pod, err := oc.KubeREST().Pods(oc.Namespace()).Get(pods[0]) |
|
| 73 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 74 |
+ |
|
| 75 |
+ g.By("expecting the pod to contain the file from the input image")
|
|
| 76 |
+ out, err := oc.Run("exec").Args(pod.Name, "-c", pod.Spec.Containers[0].Name, "--", "ls", "injected/dir").Output()
|
|
| 77 |
+ o.Expect(err).NotTo(o.HaveOccurred()) |
|
| 78 |
+ o.Expect(out).To(o.ContainSubstring("jenkins.war"))
|
|
| 39 | 79 |
}) |
| 40 | 80 |
|
| 41 | 81 |
}) |
| 82 |
+ |
|
| 42 | 83 |
}) |
| 43 | 84 |
deleted file mode 100644 |
| ... | ... |
@@ -1,175 +0,0 @@ |
| 1 |
-apiVersion: v1 |
|
| 2 |
-kind: List |
|
| 3 |
-metadata: {}
|
|
| 4 |
-items: |
|
| 5 |
-- apiVersion: v1 |
|
| 6 |
- kind: BuildConfig |
|
| 7 |
- metadata: |
|
| 8 |
- labels: |
|
| 9 |
- build: hello-builder |
|
| 10 |
- name: hello-builder |
|
| 11 |
- spec: |
|
| 12 |
- output: |
|
| 13 |
- to: |
|
| 14 |
- kind: ImageStreamTag |
|
| 15 |
- name: hello-builder:latest |
|
| 16 |
- source: |
|
| 17 |
- binary: {}
|
|
| 18 |
- type: Binary |
|
| 19 |
- strategy: |
|
| 20 |
- dockerStrategy: {}
|
|
| 21 |
- type: Docker |
|
| 22 |
- triggers: [] |
|
| 23 |
-- apiVersion: v1 |
|
| 24 |
- kind: BuildConfig |
|
| 25 |
- metadata: |
|
| 26 |
- labels: |
|
| 27 |
- build: hello-openshift |
|
| 28 |
- name: hello-openshift |
|
| 29 |
- spec: |
|
| 30 |
- output: |
|
| 31 |
- to: |
|
| 32 |
- kind: ImageStreamTag |
|
| 33 |
- name: hello-openshift:latest |
|
| 34 |
- source: |
|
| 35 |
- contextDir: examples/hello-openshift |
|
| 36 |
- git: |
|
| 37 |
- uri: https://github.com/openshift/origin.git |
|
| 38 |
- image: |
|
| 39 |
- from: |
|
| 40 |
- kind: ImageStreamTag |
|
| 41 |
- name: hello-openshift-binary:latest |
|
| 42 |
- paths: |
|
| 43 |
- - destinationDir: examples/hello-openshift/bin |
|
| 44 |
- sourcePath: /tmp/hello-openshift |
|
| 45 |
- type: Git |
|
| 46 |
- strategy: |
|
| 47 |
- dockerStrategy: {}
|
|
| 48 |
- type: Docker |
|
| 49 |
- triggers: |
|
| 50 |
- - imageChange: |
|
| 51 |
- from: |
|
| 52 |
- kind: ImageStreamTag |
|
| 53 |
- name: hello-openshift-binary:latest |
|
| 54 |
- type: ImageChange |
|
| 55 |
-- apiVersion: v1 |
|
| 56 |
- kind: BuildConfig |
|
| 57 |
- metadata: |
|
| 58 |
- labels: |
|
| 59 |
- build: hello-openshift-binary |
|
| 60 |
- name: hello-openshift-binary |
|
| 61 |
- spec: |
|
| 62 |
- output: |
|
| 63 |
- to: |
|
| 64 |
- kind: ImageStreamTag |
|
| 65 |
- name: hello-openshift-binary:latest |
|
| 66 |
- source: |
|
| 67 |
- contextDir: examples/hello-openshift |
|
| 68 |
- git: |
|
| 69 |
- uri: https://github.com/openshift/origin.git |
|
| 70 |
- type: Git |
|
| 71 |
- strategy: |
|
| 72 |
- sourceStrategy: |
|
| 73 |
- from: |
|
| 74 |
- kind: ImageStreamTag |
|
| 75 |
- name: hello-builder:latest |
|
| 76 |
- type: Source |
|
| 77 |
- triggers: |
|
| 78 |
- - imageChange: {}
|
|
| 79 |
- type: ImageChange |
|
| 80 |
- status: |
|
| 81 |
- lastVersion: 0 |
|
| 82 |
-- apiVersion: v1 |
|
| 83 |
- kind: ImageStream |
|
| 84 |
- metadata: |
|
| 85 |
- labels: |
|
| 86 |
- build: hello-builder |
|
| 87 |
- name: hello-builder |
|
| 88 |
- spec: {}
|
|
| 89 |
-- apiVersion: v1 |
|
| 90 |
- kind: ImageStream |
|
| 91 |
- metadata: |
|
| 92 |
- labels: |
|
| 93 |
- build: hello-openshift |
|
| 94 |
- name: hello-openshift |
|
| 95 |
- spec: {}
|
|
| 96 |
-- apiVersion: v1 |
|
| 97 |
- kind: ImageStream |
|
| 98 |
- metadata: |
|
| 99 |
- labels: |
|
| 100 |
- build: hello-openshift-binary |
|
| 101 |
- name: hello-openshift-binary |
|
| 102 |
- spec: {}
|
|
| 103 |
-- apiVersion: v1 |
|
| 104 |
- kind: DeploymentConfig |
|
| 105 |
- metadata: |
|
| 106 |
- labels: |
|
| 107 |
- app: hello-openshift |
|
| 108 |
- name: hello-openshift |
|
| 109 |
- spec: |
|
| 110 |
- replicas: 1 |
|
| 111 |
- selector: |
|
| 112 |
- app: hello-openshift |
|
| 113 |
- deploymentconfig: hello-openshift |
|
| 114 |
- strategy: |
|
| 115 |
- resources: {}
|
|
| 116 |
- rollingParams: |
|
| 117 |
- intervalSeconds: 1 |
|
| 118 |
- maxSurge: 25% |
|
| 119 |
- maxUnavailable: 25% |
|
| 120 |
- timeoutSeconds: 600 |
|
| 121 |
- updatePeriodSeconds: 1 |
|
| 122 |
- type: Rolling |
|
| 123 |
- template: |
|
| 124 |
- metadata: |
|
| 125 |
- labels: |
|
| 126 |
- app: hello-openshift |
|
| 127 |
- deploymentconfig: hello-openshift |
|
| 128 |
- spec: |
|
| 129 |
- containers: |
|
| 130 |
- - image: hello-openshift |
|
| 131 |
- imagePullPolicy: Always |
|
| 132 |
- readinessProbe: |
|
| 133 |
- httpGet: |
|
| 134 |
- port: 8080 |
|
| 135 |
- name: hello-openshift |
|
| 136 |
- ports: |
|
| 137 |
- - containerPort: 8080 |
|
| 138 |
- protocol: TCP |
|
| 139 |
- - containerPort: 8888 |
|
| 140 |
- protocol: TCP |
|
| 141 |
- terminationMessagePath: /dev/termination-log |
|
| 142 |
- dnsPolicy: ClusterFirst |
|
| 143 |
- restartPolicy: Always |
|
| 144 |
- securityContext: {}
|
|
| 145 |
- terminationGracePeriodSeconds: 30 |
|
| 146 |
- triggers: |
|
| 147 |
- - imageChangeParams: |
|
| 148 |
- automatic: true |
|
| 149 |
- containerNames: |
|
| 150 |
- - hello-openshift |
|
| 151 |
- from: |
|
| 152 |
- kind: ImageStreamTag |
|
| 153 |
- name: hello-openshift:latest |
|
| 154 |
- type: ImageChange |
|
| 155 |
-- apiVersion: v1 |
|
| 156 |
- kind: Service |
|
| 157 |
- metadata: |
|
| 158 |
- labels: |
|
| 159 |
- app: hello-openshift |
|
| 160 |
- name: hello-openshift |
|
| 161 |
- spec: |
|
| 162 |
- ports: |
|
| 163 |
- - name: 8080-tcp |
|
| 164 |
- port: 8080 |
|
| 165 |
- protocol: TCP |
|
| 166 |
- targetPort: 8080 |
|
| 167 |
- - name: 8888-tcp |
|
| 168 |
- port: 8888 |
|
| 169 |
- protocol: TCP |
|
| 170 |
- targetPort: 8888 |
|
| 171 |
- selector: |
|
| 172 |
- app: hello-openshift |
|
| 173 |
- deploymentconfig: hello-openshift |
|
| 174 |
- sessionAffinity: None |
|
| 175 |
- type: ClusterIP |
| 176 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,162 @@ |
| 0 |
+apiVersion: v1 |
|
| 1 |
+kind: List |
|
| 2 |
+metadata: {}
|
|
| 3 |
+items: |
|
| 4 |
+- apiVersion: v1 |
|
| 5 |
+ kind: BuildConfig |
|
| 6 |
+ metadata: |
|
| 7 |
+ labels: |
|
| 8 |
+ build: imagesourcebuild |
|
| 9 |
+ name: imagesourcebuild |
|
| 10 |
+ spec: |
|
| 11 |
+ output: |
|
| 12 |
+ to: |
|
| 13 |
+ kind: ImageStreamTag |
|
| 14 |
+ name: imagesourceapp:latest |
|
| 15 |
+ source: |
|
| 16 |
+ git: |
|
| 17 |
+ uri: https://github.com/openshift/ruby-hello-world.git |
|
| 18 |
+ images: |
|
| 19 |
+ - from: |
|
| 20 |
+ kind: ImageStreamTag |
|
| 21 |
+ name: jenkins:latest |
|
| 22 |
+ namespace: openshift |
|
| 23 |
+ paths: |
|
| 24 |
+ - destinationDir: injected/dir |
|
| 25 |
+ sourcePath: /usr/lib/jenkins/jenkins.war |
|
| 26 |
+ strategy: |
|
| 27 |
+ sourceStrategy: |
|
| 28 |
+ forcePull: true |
|
| 29 |
+ from: |
|
| 30 |
+ kind: ImageStreamTag |
|
| 31 |
+ name: ruby:latest |
|
| 32 |
+ namespace: openshift |
|
| 33 |
+- apiVersion: v1 |
|
| 34 |
+ kind: BuildConfig |
|
| 35 |
+ metadata: |
|
| 36 |
+ labels: |
|
| 37 |
+ build: imagedockerbuild |
|
| 38 |
+ name: imagedockerbuild |
|
| 39 |
+ spec: |
|
| 40 |
+ output: |
|
| 41 |
+ to: |
|
| 42 |
+ kind: ImageStreamTag |
|
| 43 |
+ name: imagedockerapp:latest |
|
| 44 |
+ source: |
|
| 45 |
+ git: |
|
| 46 |
+ uri: https://github.com/openshift/ruby-hello-world.git |
|
| 47 |
+ images: |
|
| 48 |
+ - from: |
|
| 49 |
+ kind: ImageStreamTag |
|
| 50 |
+ name: jenkins:latest |
|
| 51 |
+ namespace: openshift |
|
| 52 |
+ paths: |
|
| 53 |
+ - destinationDir: injected/dir |
|
| 54 |
+ sourcePath: /usr/lib/jenkins/jenkins.war |
|
| 55 |
+ strategy: |
|
| 56 |
+ sourceStrategy: |
|
| 57 |
+ forcePull: true |
|
| 58 |
+ from: |
|
| 59 |
+ kind: ImageStreamTag |
|
| 60 |
+ name: ruby:latest |
|
| 61 |
+ namespace: openshift |
|
| 62 |
+- apiVersion: v1 |
|
| 63 |
+ kind: ImageStream |
|
| 64 |
+ metadata: |
|
| 65 |
+ name: imagesourceapp |
|
| 66 |
+ spec: {}
|
|
| 67 |
+- apiVersion: v1 |
|
| 68 |
+ kind: ImageStream |
|
| 69 |
+ metadata: |
|
| 70 |
+ name: imagedockerapp |
|
| 71 |
+ spec: {}
|
|
| 72 |
+ |
|
| 73 |
+- apiVersion: v1 |
|
| 74 |
+ kind: DeploymentConfig |
|
| 75 |
+ metadata: |
|
| 76 |
+ name: imagesourceapp |
|
| 77 |
+ spec: |
|
| 78 |
+ replicas: 1 |
|
| 79 |
+ selector: |
|
| 80 |
+ app: imagesourceapp |
|
| 81 |
+ deploymentconfig: imagesourceapp |
|
| 82 |
+ strategy: |
|
| 83 |
+ type: Rolling |
|
| 84 |
+ template: |
|
| 85 |
+ metadata: |
|
| 86 |
+ labels: |
|
| 87 |
+ app: imagesourceapp |
|
| 88 |
+ deploymentconfig: imagesourceapp |
|
| 89 |
+ spec: |
|
| 90 |
+ containers: |
|
| 91 |
+ - image: imagesourceapp |
|
| 92 |
+ imagePullPolicy: Always |
|
| 93 |
+ readinessProbe: |
|
| 94 |
+ httpGet: |
|
| 95 |
+ port: 8080 |
|
| 96 |
+ name: imagesourceapp |
|
| 97 |
+ ports: |
|
| 98 |
+ - containerPort: 8080 |
|
| 99 |
+ protocol: TCP |
|
| 100 |
+ - containerPort: 8888 |
|
| 101 |
+ protocol: TCP |
|
| 102 |
+ terminationMessagePath: /dev/termination-log |
|
| 103 |
+ dnsPolicy: ClusterFirst |
|
| 104 |
+ restartPolicy: Always |
|
| 105 |
+ securityContext: {}
|
|
| 106 |
+ terminationGracePeriodSeconds: 30 |
|
| 107 |
+ triggers: |
|
| 108 |
+ - imageChangeParams: |
|
| 109 |
+ automatic: true |
|
| 110 |
+ containerNames: |
|
| 111 |
+ - imagesourceapp |
|
| 112 |
+ from: |
|
| 113 |
+ kind: ImageStreamTag |
|
| 114 |
+ name: imagesourceapp:latest |
|
| 115 |
+ type: ImageChange |
|
| 116 |
+ |
|
| 117 |
+ |
|
| 118 |
+- apiVersion: v1 |
|
| 119 |
+ kind: DeploymentConfig |
|
| 120 |
+ metadata: |
|
| 121 |
+ name: imagedockerapp |
|
| 122 |
+ spec: |
|
| 123 |
+ replicas: 1 |
|
| 124 |
+ selector: |
|
| 125 |
+ app: imagedockerapp |
|
| 126 |
+ deploymentconfig: imagedockerapp |
|
| 127 |
+ strategy: |
|
| 128 |
+ type: Rolling |
|
| 129 |
+ template: |
|
| 130 |
+ metadata: |
|
| 131 |
+ labels: |
|
| 132 |
+ app: imagedockerapp |
|
| 133 |
+ deploymentconfig: imagedockerapp |
|
| 134 |
+ spec: |
|
| 135 |
+ containers: |
|
| 136 |
+ - image: imagedockerapp |
|
| 137 |
+ imagePullPolicy: Always |
|
| 138 |
+ readinessProbe: |
|
| 139 |
+ httpGet: |
|
| 140 |
+ port: 8080 |
|
| 141 |
+ name: imagedockerapp |
|
| 142 |
+ ports: |
|
| 143 |
+ - containerPort: 8080 |
|
| 144 |
+ protocol: TCP |
|
| 145 |
+ - containerPort: 8888 |
|
| 146 |
+ protocol: TCP |
|
| 147 |
+ terminationMessagePath: /dev/termination-log |
|
| 148 |
+ dnsPolicy: ClusterFirst |
|
| 149 |
+ restartPolicy: Always |
|
| 150 |
+ securityContext: {}
|
|
| 151 |
+ terminationGracePeriodSeconds: 30 |
|
| 152 |
+ triggers: |
|
| 153 |
+ - imageChangeParams: |
|
| 154 |
+ automatic: true |
|
| 155 |
+ containerNames: |
|
| 156 |
+ - imagedockerapp |
|
| 157 |
+ from: |
|
| 158 |
+ kind: ImageStreamTag |
|
| 159 |
+ name: imagedockerapp:latest |
|
| 160 |
+ type: ImageChange |
|
| 161 |
+ |
|
| 0 | 162 |
\ No newline at end of file |
| ... | ... |
@@ -142,7 +142,7 @@ func WaitForAnImageStream(client client.ImageStreamInterface, |
| 142 | 142 |
return nil |
| 143 | 143 |
} |
| 144 | 144 |
if isFailed(&list.Items[i]) {
|
| 145 |
- return fmt.Errorf("The deployment %q status is %q",
|
|
| 145 |
+ return fmt.Errorf("The image stream %q status is %q",
|
|
| 146 | 146 |
name, list.Items[i].Annotations[imageapi.DockerImageRepositoryCheckAnnotation]) |
| 147 | 147 |
} |
| 148 | 148 |
} |