Browse code

oc: Use default resources where it makes sense

kargakis authored on 2015/09/30 00:45:46
Showing 12 changed files
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	kapi "k8s.io/kubernetes/pkg/api"
10 10
 	"k8s.io/kubernetes/pkg/api/errors"
11 11
 	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
12
+	"k8s.io/kubernetes/pkg/kubectl/resource"
12 13
 
13 14
 	buildapi "github.com/openshift/origin/pkg/build/api"
14 15
 	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
... ...
@@ -68,11 +69,20 @@ func RunCancelBuild(f *clientcmd.Factory, out io.Writer, cmd *cobra.Command, arg
68 68
 		return err
69 69
 	}
70 70
 	buildClient := client.Builds(namespace)
71
-	build, err := buildClient.Get(buildName)
71
+
72
+	mapper, typer := f.Object()
73
+	obj, err := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
74
+		NamespaceParam(namespace).
75
+		ResourceNames("builds", buildName).
76
+		SingleResourceType().
77
+		Do().Object()
72 78
 	if err != nil {
73 79
 		return err
74 80
 	}
75
-
81
+	build, ok := obj.(*buildapi.Build)
82
+	if !ok {
83
+		return fmt.Errorf("%q is not a valid build", buildName)
84
+	}
76 85
 	if !isBuildCancellable(build) {
77 86
 		return nil
78 87
 	}
... ...
@@ -120,7 +120,7 @@ func NewCmdDeploy(fullName string, f *clientcmd.Factory, out io.Writer) *cobra.C
120 120
 
121 121
 func (o *DeployOptions) Complete(f *clientcmd.Factory, args []string, out io.Writer) error {
122 122
 	if len(args) > 1 {
123
-		return errors.New("only one deploymentConfig name is supported as argument.")
123
+		return errors.New("only one deployment config name is supported as argument.")
124 124
 	}
125 125
 	var err error
126 126
 
... ...
@@ -139,11 +139,7 @@ func (o *DeployOptions) Complete(f *clientcmd.Factory, args []string, out io.Wri
139 139
 	o.out = out
140 140
 
141 141
 	if len(args) > 0 {
142
-		name := args[0]
143
-		if strings.Index(name, "/") == -1 {
144
-			name = fmt.Sprintf("dc/%s", name)
145
-		}
146
-		o.deploymentConfigName = name
142
+		o.deploymentConfigName = args[0]
147 143
 	}
148 144
 
149 145
 	return nil
... ...
@@ -151,7 +147,7 @@ func (o *DeployOptions) Complete(f *clientcmd.Factory, args []string, out io.Wri
151 151
 
152 152
 func (o DeployOptions) Validate() error {
153 153
 	if len(o.deploymentConfigName) == 0 {
154
-		return errors.New("a deploymentConfig name is required.")
154
+		return errors.New("a deployment config name is required.")
155 155
 	}
156 156
 	numOptions := 0
157 157
 	if o.deployLatest {
... ...
@@ -175,7 +171,7 @@ func (o DeployOptions) Validate() error {
175 175
 func (o DeployOptions) RunDeploy() error {
176 176
 	r := o.builder.
177 177
 		NamespaceParam(o.namespace).
178
-		ResourceTypeOrNameArgs(false, o.deploymentConfigName).
178
+		ResourceNames("deploymentconfigs", o.deploymentConfigName).
179 179
 		SingleResourceType().
180 180
 		Do()
181 181
 	resultObj, err := r.Object()
... ...
@@ -184,7 +180,7 @@ func (o DeployOptions) RunDeploy() error {
184 184
 	}
185 185
 	config, ok := resultObj.(*deployapi.DeploymentConfig)
186 186
 	if !ok {
187
-		return fmt.Errorf("%s is not a valid deploymentconfig", o.deploymentConfigName)
187
+		return fmt.Errorf("%s is not a valid deployment config", o.deploymentConfigName)
188 188
 	}
189 189
 
190 190
 	switch {
... ...
@@ -27,6 +27,7 @@ import (
27 27
 
28 28
 	buildapi "github.com/openshift/origin/pkg/build/api"
29 29
 	osclient "github.com/openshift/origin/pkg/client"
30
+	osutil "github.com/openshift/origin/pkg/cmd/util"
30 31
 	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
31 32
 	"github.com/openshift/origin/pkg/generate/git"
32 33
 	"github.com/openshift/source-to-image/pkg/tar"
... ...
@@ -129,15 +130,37 @@ func RunStartBuild(f *clientcmd.Factory, in io.Reader, out io.Writer, cmd *cobra
129 129
 		return cmdutil.UsageError(cmd, "Must pass a name of a build config or specify build name with '--from-build' flag")
130 130
 	}
131 131
 
132
-	name := buildName
133
-	isBuild := true
132
+	namespace, _, err := f.DefaultNamespace()
133
+	if err != nil {
134
+		return err
135
+	}
136
+
137
+	var (
138
+		name     = buildName
139
+		resource = "builds"
140
+	)
141
+
142
+	if len(name) == 0 && len(args) > 0 && len(args[0]) > 0 {
143
+		mapper, _ := f.Object()
144
+		resource, name, err = osutil.ResolveResource("buildconfigs", args[0], mapper)
145
+		if err != nil {
146
+			return err
147
+		}
148
+		switch resource {
149
+		case "buildconfigs":
150
+			// no special handling required
151
+		case "builds":
152
+			return fmt.Errorf("use --from-build to rerun your builds")
153
+		default:
154
+			return fmt.Errorf("invalid resource provided: %s", resource)
155
+		}
156
+	}
134 157
 	if len(name) == 0 {
135
-		name = args[0]
136
-		isBuild = false
158
+		return fmt.Errorf("a resource name is required either as an argument or by using --from-build")
137 159
 	}
138 160
 
139 161
 	if webhooks.Provided() {
140
-		return RunListBuildWebHooks(f, out, cmd.Out(), name, isBuild, webhooks.String())
162
+		return RunListBuildWebHooks(f, out, cmd.Out(), name, resource, webhooks.String())
141 163
 	}
142 164
 
143 165
 	client, _, err := f.Clients()
... ...
@@ -145,11 +168,6 @@ func RunStartBuild(f *clientcmd.Factory, in io.Reader, out io.Writer, cmd *cobra
145 145
 		return err
146 146
 	}
147 147
 
148
-	namespace, _, err := f.DefaultNamespace()
149
-	if err != nil {
150
-		return err
151
-	}
152
-
153 148
 	request := &buildapi.BuildRequest{
154 149
 		ObjectMeta: kapi.ObjectMeta{Name: name},
155 150
 	}
... ...
@@ -166,7 +184,7 @@ func RunStartBuild(f *clientcmd.Factory, in io.Reader, out io.Writer, cmd *cobra
166 166
 
167 167
 	var newBuild *buildapi.Build
168 168
 	switch {
169
-	case !isBuild && (len(fromFile) > 0 || len(fromDir) > 0 || len(fromRepo) > 0):
169
+	case len(args) > 0 && (len(fromFile) > 0 || len(fromDir) > 0 || len(fromRepo) > 0):
170 170
 		request := &buildapi.BinaryBuildRequestOptions{
171 171
 			ObjectMeta: kapi.ObjectMeta{
172 172
 				Name:      name,
... ...
@@ -177,16 +195,16 @@ func RunStartBuild(f *clientcmd.Factory, in io.Reader, out io.Writer, cmd *cobra
177 177
 		if newBuild, err = streamPathToBuild(git, in, cmd.Out(), client.BuildConfigs(namespace), fromDir, fromFile, fromRepo, request); err != nil {
178 178
 			return err
179 179
 		}
180
-
181
-	case isBuild:
180
+	case resource == "builds":
182 181
 		if newBuild, err = client.Builds(namespace).Clone(request); err != nil {
183 182
 			return err
184 183
 		}
185
-
186
-	default:
184
+	case resource == "buildconfigs":
187 185
 		if newBuild, err = client.BuildConfigs(namespace).Instantiate(request); err != nil {
188 186
 			return err
189 187
 		}
188
+	default:
189
+		return fmt.Errorf("invalid resource provided: %s", resource)
190 190
 	}
191 191
 
192 192
 	fmt.Fprintln(out, newBuild.Name)
... ...
@@ -240,7 +258,7 @@ func RunStartBuild(f *clientcmd.Factory, in io.Reader, out io.Writer, cmd *cobra
240 240
 }
241 241
 
242 242
 // RunListBuildWebHooks prints the webhooks for the provided build config.
243
-func RunListBuildWebHooks(f *clientcmd.Factory, out, errOut io.Writer, name string, isBuild bool, webhookFilter string) error {
243
+func RunListBuildWebHooks(f *clientcmd.Factory, out, errOut io.Writer, name, resource, webhookFilter string) error {
244 244
 	generic, github := false, false
245 245
 	prefix := false
246 246
 	switch webhookFilter {
... ...
@@ -263,7 +281,10 @@ func RunListBuildWebHooks(f *clientcmd.Factory, out, errOut io.Writer, name stri
263 263
 		return err
264 264
 	}
265 265
 
266
-	if isBuild {
266
+	switch resource {
267
+	case "buildconfigs":
268
+		// no special handling required
269
+	case "builds":
267 270
 		build, err := client.Builds(namespace).Get(name)
268 271
 		if err != nil {
269 272
 			return err
... ...
@@ -276,7 +297,10 @@ func RunListBuildWebHooks(f *clientcmd.Factory, out, errOut io.Writer, name stri
276 276
 			namespace = ref.Namespace
277 277
 		}
278 278
 		name = ref.Name
279
+	default:
280
+		return fmt.Errorf("invalid resource provided: %s", resource)
279 281
 	}
282
+
280 283
 	config, err := client.BuildConfigs(namespace).Get(name)
281 284
 	if err != nil {
282 285
 		return err
... ...
@@ -162,7 +162,7 @@ func (o AddSecretOptions) Validate() error {
162 162
 func (o AddSecretOptions) AddSecrets() error {
163 163
 	r := resource.NewBuilder(o.Mapper, o.Typer, o.ClientMapper).
164 164
 		NamespaceParam(o.Namespace).
165
-		ResourceTypeOrNameArgs(false, o.TargetName).
165
+		ResourceNames("serviceaccounts", o.TargetName).
166 166
 		SingleResourceType().
167 167
 		Do()
168 168
 	if r.Err() != nil {
... ...
@@ -223,7 +223,7 @@ func (o AddSecretOptions) addSecretsToServiceAccount(serviceaccount *kapi.Servic
223 223
 func (o AddSecretOptions) getSecrets() ([]*kapi.Secret, error) {
224 224
 	r := resource.NewBuilder(o.Mapper, o.Typer, o.ClientMapper).
225 225
 		NamespaceParam(o.Namespace).
226
-		ResourceTypeOrNameArgs(false, o.SecretNames...).
226
+		ResourceNames("secrets", o.SecretNames...).
227 227
 		SingleResourceType().
228 228
 		Do()
229 229
 	if r.Err() != nil {
... ...
@@ -14,6 +14,7 @@ import (
14 14
 
15 15
 	"github.com/openshift/origin/pkg/client"
16 16
 	"github.com/openshift/origin/pkg/cmd/cli/describe"
17
+	osutil "github.com/openshift/origin/pkg/cmd/util"
17 18
 	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
18 19
 	imageapi "github.com/openshift/origin/pkg/image/api"
19 20
 	imagegraph "github.com/openshift/origin/pkg/image/graph/nodes"
... ...
@@ -43,7 +44,6 @@ const BuildChainRecommendedCommandName = "build-chain"
43 43
 // BuildChainOptions contains all the options needed for build-chain
44 44
 type BuildChainOptions struct {
45 45
 	name string
46
-	tag  string
47 46
 
48 47
 	defaultNamespace string
49 48
 	namespaces       sets.String
... ...
@@ -62,7 +62,7 @@ func NewCmdBuildChain(name, fullName string, f *clientcmd.Factory, out io.Writer
62 62
 		namespaces: sets.NewString(),
63 63
 	}
64 64
 	cmd := &cobra.Command{
65
-		Use:     "build-chain [IMAGESTREAM:TAG]",
65
+		Use:     "build-chain IMAGESTREAMTAG",
66 66
 		Short:   "Output the inputs and dependencies of your builds",
67 67
 		Long:    buildChainLong,
68 68
 		Example: fmt.Sprintf(buildChainExample, fullName),
... ...
@@ -84,7 +84,7 @@ func NewCmdBuildChain(name, fullName string, f *clientcmd.Factory, out io.Writer
84 84
 // Complete completes the required options for build-chain
85 85
 func (o *BuildChainOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, args []string, out io.Writer) error {
86 86
 	if len(args) != 1 {
87
-		return cmdutil.UsageError(cmd, "Must pass an image stream name and optionally a tag. In case of an empty tag, 'latest' will be used.")
87
+		return cmdutil.UsageError(cmd, "Must pass an image stream tag. If only an image stream name is specified, 'latest' will be used for the tag.")
88 88
 	}
89 89
 
90 90
 	// Setup client
... ...
@@ -94,12 +94,20 @@ func (o *BuildChainOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, a
94 94
 	}
95 95
 	o.c, o.t = oc, oc
96 96
 
97
-	// Parse user input
98
-	o.name, o.tag, err = buildChainInput(args[0])
97
+	resource := ""
98
+	mapper, _ := f.Object()
99
+	resource, o.name, err = osutil.ResolveResource("imagestreamtags", args[0], mapper)
99 100
 	if err != nil {
100
-		return cmdutil.UsageError(cmd, err.Error())
101
+		return err
102
+	}
103
+
104
+	switch resource {
105
+	case "imagestreamtags":
106
+		o.name = imageapi.NormalizeImageStreamTag(o.name)
107
+		glog.V(4).Infof("Using %q as the image stream tag to look dependencies for", o.name)
108
+	default:
109
+		return fmt.Errorf("invalid resource provided: %s", resource)
101 110
 	}
102
-	glog.V(4).Infof("Using '%s:%s' as the image stream tag to look dependencies for", o.name, o.tag)
103 111
 
104 112
 	// Setup namespace
105 113
 	if o.allNamespaces {
... ...
@@ -120,7 +128,7 @@ func (o *BuildChainOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, a
120 120
 	}
121 121
 
122 122
 	o.defaultNamespace = namespace
123
-	glog.V(4).Infof("Using %q as the namespace for '%s:%s'", o.defaultNamespace, o.name, o.tag)
123
+	glog.V(4).Infof("Using %q as the namespace for %q", o.defaultNamespace, o.name)
124 124
 	o.namespaces.Insert(namespace)
125 125
 	glog.V(4).Infof("Will look for deps in %s", strings.Join(o.namespaces.List(), ","))
126 126
 
... ...
@@ -130,10 +138,7 @@ func (o *BuildChainOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, a
130 130
 // Validate returns validation errors regarding build-chain
131 131
 func (o *BuildChainOptions) Validate() error {
132 132
 	if len(o.name) == 0 {
133
-		return fmt.Errorf("image stream name cannot be empty")
134
-	}
135
-	if len(o.tag) == 0 {
136
-		o.tag = imageapi.DefaultImageTag
133
+		return fmt.Errorf("image stream tag cannot be empty")
137 134
 	}
138 135
 	if len(o.defaultNamespace) == 0 {
139 136
 		return fmt.Errorf("default namespace cannot be empty")
... ...
@@ -153,15 +158,17 @@ func (o *BuildChainOptions) Validate() error {
153 153
 // RunBuildChain contains all the necessary functionality for the OpenShift
154 154
 // experimental build-chain command
155 155
 func (o *BuildChainOptions) RunBuildChain() error {
156
-	ist := imagegraph.MakeImageStreamTagObjectMeta(o.defaultNamespace, o.name, o.tag)
156
+	ist := imagegraph.MakeImageStreamTagObjectMeta2(o.defaultNamespace, o.name)
157
+
157 158
 	desc, err := describe.NewChainDescriber(o.c, o.namespaces, o.output).Describe(ist, !o.triggerOnly)
158 159
 	if err != nil {
159 160
 		if _, isNotFoundErr := err.(describe.NotFoundErr); isNotFoundErr {
161
+			name, tag, _ := imageapi.SplitImageStreamTag(o.name)
160 162
 			// Try to get the imageStreamTag via a direct GET
161
-			if _, getErr := o.t.ImageStreamTags(o.defaultNamespace).Get(o.name, o.tag); getErr != nil {
163
+			if _, getErr := o.t.ImageStreamTags(o.defaultNamespace).Get(name, tag); getErr != nil {
162 164
 				return getErr
163 165
 			}
164
-			fmt.Printf("Image stream tag '%s:%s' in %q doesn't have any dependencies.\n", o.name, o.tag, o.defaultNamespace)
166
+			fmt.Printf("Image stream tag %q in %q doesn't have any dependencies.\n", o.name, o.defaultNamespace)
165 167
 			return nil
166 168
 		}
167 169
 		return err
... ...
@@ -171,26 +178,3 @@ func (o *BuildChainOptions) RunBuildChain() error {
171 171
 
172 172
 	return nil
173 173
 }
174
-
175
-// buildChainInput parses user input and returns a stream name, a tag
176
-// and an error if any
177
-func buildChainInput(input string) (string, string, error) {
178
-	// Split name and tag
179
-	name, tag, _ := imageapi.SplitImageStreamTag(input)
180
-
181
-	// Support resource type/name syntax
182
-	// TODO: Use the RESTMapper to resolve this
183
-	resource := strings.Split(name, "/")
184
-	switch len(resource) {
185
-	case 1:
186
-	case 2:
187
-		resourceType := resource[0]
188
-		if resourceType != "istag" && resourceType != "imagestreamtag" {
189
-			return "", "", fmt.Errorf("invalid resource type %q", resourceType)
190
-		}
191
-	default:
192
-		return "", "", fmt.Errorf("invalid image stream name %q", name)
193
-	}
194
-
195
-	return name, tag, nil
196
-}
... ...
@@ -1,6 +1,7 @@
1 1
 package util
2 2
 
3 3
 import (
4
+	"errors"
4 5
 	"fmt"
5 6
 	"io"
6 7
 	"path/filepath"
... ...
@@ -8,6 +9,7 @@ import (
8 8
 
9 9
 	"github.com/spf13/cobra"
10 10
 
11
+	"k8s.io/kubernetes/pkg/api/meta"
11 12
 	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
12 13
 )
13 14
 
... ...
@@ -31,3 +33,30 @@ func GetDisplayFilename(filename string) string {
31 31
 
32 32
 	return filename
33 33
 }
34
+
35
+// ResolveResource returns the resource type and name of the resourceString.
36
+// If the resource string has no specified type, defaultResource will be returned.
37
+func ResolveResource(defaultResource, resourceString string, mapper meta.RESTMapper) (string, string, error) {
38
+	if mapper == nil {
39
+		return "", "", errors.New("mapper cannot be nil")
40
+	}
41
+
42
+	var name string
43
+	parts := strings.Split(resourceString, "/")
44
+	switch len(parts) {
45
+	case 1:
46
+		name = parts[0]
47
+	case 2:
48
+		_, kind, err := mapper.VersionAndKindForResource(parts[0])
49
+		if err != nil {
50
+			return "", "", err
51
+		}
52
+		name = parts[1]
53
+		resource, _ := meta.KindToResource(kind, false)
54
+		return resource, name, nil
55
+	default:
56
+		return "", "", fmt.Errorf("invalid resource format: %s", resourceString)
57
+	}
58
+
59
+	return defaultResource, name, nil
60
+}
34 61
new file mode 100644
... ...
@@ -0,0 +1,181 @@
0
+package util_test
1
+
2
+import (
3
+	"testing"
4
+
5
+	"k8s.io/kubernetes/pkg/kubectl"
6
+
7
+	"github.com/openshift/origin/pkg/api/latest"
8
+	"github.com/openshift/origin/pkg/cmd/util"
9
+	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
10
+)
11
+
12
+func TestResolveResource(t *testing.T) {
13
+	mapper := clientcmd.ShortcutExpander{RESTMapper: kubectl.ShortcutExpander{RESTMapper: latest.RESTMapper}}
14
+
15
+	tests := []struct {
16
+		name             string
17
+		defaultResource  string
18
+		resourceString   string
19
+		expectedResource string
20
+		expectedName     string
21
+		expectedErr      bool
22
+	}{
23
+		{
24
+			name:             "invalid case #1",
25
+			defaultResource:  "",
26
+			resourceString:   "a/b/c",
27
+			expectedResource: "",
28
+			expectedName:     "",
29
+			expectedErr:      true,
30
+		},
31
+		{
32
+			name:             "invalid case #2",
33
+			defaultResource:  "",
34
+			resourceString:   "foo/bar",
35
+			expectedResource: "",
36
+			expectedName:     "",
37
+			expectedErr:      true,
38
+		},
39
+		{
40
+			name:             "empty resource string case #1",
41
+			defaultResource:  "",
42
+			resourceString:   "",
43
+			expectedResource: "",
44
+			expectedName:     "",
45
+			expectedErr:      false,
46
+		},
47
+		{
48
+			name:             "empty resource string case #2",
49
+			defaultResource:  "",
50
+			resourceString:   "bar",
51
+			expectedResource: "",
52
+			expectedName:     "bar",
53
+			expectedErr:      false,
54
+		},
55
+		{
56
+			name:             "empty resource string case #3",
57
+			defaultResource:  "foo",
58
+			resourceString:   "bar",
59
+			expectedResource: "foo",
60
+			expectedName:     "bar",
61
+			expectedErr:      false,
62
+		},
63
+		{
64
+			name:             "(KUBE) short name",
65
+			defaultResource:  "foo",
66
+			resourceString:   "rc/bar",
67
+			expectedResource: "replicationcontrollers",
68
+			expectedName:     "bar",
69
+			expectedErr:      false,
70
+		},
71
+		{
72
+			name:             "(KUBE) long name, case insensitive #1",
73
+			defaultResource:  "foo",
74
+			resourceString:   "replicationcontroller/bar",
75
+			expectedResource: "replicationcontrollers",
76
+			expectedName:     "bar",
77
+			expectedErr:      false,
78
+		},
79
+		{
80
+			name:             "(KUBE) long name, case insensitive #2",
81
+			defaultResource:  "foo",
82
+			resourceString:   "replicationcontrollers/bar",
83
+			expectedResource: "replicationcontrollers",
84
+			expectedName:     "bar",
85
+			expectedErr:      false,
86
+		},
87
+		{
88
+			name:             "(KUBE) long name, case insensitive #3",
89
+			defaultResource:  "foo",
90
+			resourceString:   "ReplicationControllers/bar",
91
+			expectedResource: "replicationcontrollers",
92
+			expectedName:     "bar",
93
+			expectedErr:      false,
94
+		},
95
+		{
96
+			name:             "(KUBE) long name, case insensitive #4",
97
+			defaultResource:  "foo",
98
+			resourceString:   "ReplicationControllers/bar",
99
+			expectedResource: "replicationcontrollers",
100
+			expectedName:     "bar",
101
+			expectedErr:      false,
102
+		},
103
+		{
104
+			name:             "(KUBE) long name, case insensitive #5",
105
+			defaultResource:  "foo",
106
+			resourceString:   "ReplicationControllers/Bar",
107
+			expectedResource: "replicationcontrollers",
108
+			expectedName:     "Bar",
109
+			expectedErr:      false,
110
+		},
111
+		{
112
+			name:             "(ORIGIN) short name",
113
+			defaultResource:  "foo",
114
+			resourceString:   "bc/bar",
115
+			expectedResource: "buildconfigs",
116
+			expectedName:     "bar",
117
+			expectedErr:      false,
118
+		},
119
+		{
120
+			name:             "(ORIGIN) long name, case insensitive #1",
121
+			defaultResource:  "foo",
122
+			resourceString:   "buildconfig/bar",
123
+			expectedResource: "buildconfigs",
124
+			expectedName:     "bar",
125
+			expectedErr:      false,
126
+		},
127
+		{
128
+			name:             "(ORIGIN) long name, case insensitive #2",
129
+			defaultResource:  "foo",
130
+			resourceString:   "buildconfigs/bar",
131
+			expectedResource: "buildconfigs",
132
+			expectedName:     "bar",
133
+			expectedErr:      false,
134
+		},
135
+		{
136
+			name:             "(ORIGIN) long name, case insensitive #3",
137
+			defaultResource:  "foo",
138
+			resourceString:   "BuildConfigs/bar",
139
+			expectedResource: "buildconfigs",
140
+			expectedName:     "bar",
141
+			expectedErr:      false,
142
+		},
143
+		{
144
+			name:             "(ORIGIN) long name, case insensitive #4",
145
+			defaultResource:  "foo",
146
+			resourceString:   "BuildConfigs/bar",
147
+			expectedResource: "buildconfigs",
148
+			expectedName:     "bar",
149
+			expectedErr:      false,
150
+		},
151
+		{
152
+			name:             "(ORIGIN) long name, case insensitive #5",
153
+			defaultResource:  "foo",
154
+			resourceString:   "BuildConfigs/Bar",
155
+			expectedResource: "buildconfigs",
156
+			expectedName:     "Bar",
157
+			expectedErr:      false,
158
+		},
159
+	}
160
+
161
+	for _, test := range tests {
162
+		gotResource, gotName, gotErr := util.ResolveResource(test.defaultResource, test.resourceString, mapper)
163
+		if gotErr != nil && !test.expectedErr {
164
+			t.Errorf("%s: expected no error, got %v", test.name, gotErr)
165
+			continue
166
+		}
167
+		if gotErr == nil && test.expectedErr {
168
+			t.Errorf("%s: expected error but got none", test.name)
169
+			continue
170
+		}
171
+		if gotResource != test.expectedResource {
172
+			t.Errorf("%s: expected resource type %s, got %s", test.name, test.expectedResource, gotResource)
173
+			continue
174
+		}
175
+		if gotName != test.expectedName {
176
+			t.Errorf("%s: expected resource name %s, got %s", test.name, test.expectedName, gotName)
177
+			continue
178
+		}
179
+	}
180
+}
... ...
@@ -210,6 +210,16 @@ func JoinImageStreamTag(name, tag string) string {
210 210
 	return fmt.Sprintf("%s:%s", name, tag)
211 211
 }
212 212
 
213
+// NormalizeImageStreamTag normalizes an image stream tag by defaulting to 'latest'
214
+// if no tag has been specified.
215
+func NormalizeImageStreamTag(name string) string {
216
+	if !strings.Contains(name, ":") {
217
+		// Default to latest
218
+		return JoinImageStreamTag(name, DefaultImageTag)
219
+	}
220
+	return name
221
+}
222
+
213 223
 // ImageWithMetadata returns a copy of image with the DockerImageMetadata filled in
214 224
 // from the raw DockerImageManifest data stored in the image.
215 225
 func ImageWithMetadata(image Image) (*Image, error) {
... ...
@@ -173,6 +173,9 @@ echo "registry: ok"
173 173
 
174 174
 # Test building a dependency tree
175 175
 oc process -f examples/sample-app/application-template-stibuild.json -l build=sti | oc create -f -
176
+# Test both the type/name resource syntax and the fact that istag/origin-ruby-sample:latest is still
177
+# not created but due to a buildConfig pointing to it, we get back its graph of deps.
178
+[ "$(oadm build-chain istag/origin-ruby-sample | grep 'imagestreamtag/origin-ruby-sample:latest')" ]
176 179
 [ "$(oadm build-chain ruby-20-centos7 -o dot | grep 'graph')" ]
177 180
 oc delete all -l build=sti
178 181
 echo "ex build-chain: ok"
... ...
@@ -26,16 +26,16 @@ oc get buildConfigs
26 26
 oc get bc
27 27
 oc get builds
28 28
 
29
-REAL_OUTPUT_TO=$(oc get bc/ruby-sample-build -t '{{ .spec.output.to.name }}')
29
+REAL_OUTPUT_TO=$(oc get bc/ruby-sample-build --template='{{ .spec.output.to.name }}')
30 30
 oc patch bc/ruby-sample-build -p '{"spec":{"output":{"to":{"name":"different:tag1"}}}}'
31
-oc get bc/ruby-sample-build -t '{{ .spec.output.to.name }}' | grep 'different'
31
+oc get bc/ruby-sample-build --template='{{ .spec.output.to.name }}' | grep 'different'
32 32
 oc patch bc/ruby-sample-build -p "{\"spec\":{\"output\":{\"to\":{\"name\":\"${REAL_OUTPUT_TO}\"}}}}"
33 33
 echo "patchAnonFields: ok"
34 34
 
35 35
 [ "$(oc describe buildConfigs ruby-sample-build | grep --text "Webhook GitHub" | grep -F "${url}/oapi/v1/namespaces/${project}/buildconfigs/ruby-sample-build/webhooks/secret101/github")" ]
36 36
 [ "$(oc describe buildConfigs ruby-sample-build | grep --text "Webhook Generic" | grep -F "${url}/oapi/v1/namespaces/${project}/buildconfigs/ruby-sample-build/webhooks/secret101/generic")" ]
37 37
 oc start-build --list-webhooks='all' ruby-sample-build
38
-[ "$(oc start-build --list-webhooks='all' ruby-sample-build | grep --text "generic")" ]
38
+[ "$(oc start-build --list-webhooks='all' bc/ruby-sample-build | grep --text "generic")" ]
39 39
 [ "$(oc start-build --list-webhooks='all' ruby-sample-build | grep --text "github")" ]
40 40
 [ "$(oc start-build --list-webhooks='github' ruby-sample-build | grep --text "secret101")" ]
41 41
 [ ! "$(oc start-build --list-webhooks='blah')" ]
... ...
@@ -50,11 +50,15 @@ oc create -f test/integration/fixtures/test-buildcli.json
50 50
 # the build should use the image field as defined in the buildconfig
51 51
 started=$(oc start-build ruby-sample-build-invalidtag)
52 52
 oc describe build ${started} | grep openshift/ruby-20-centos7$
53
+frombuild=$(oc start-build --from-build="${started}")
54
+oc describe build ${frombuild} | grep openshift/ruby-20-centos7$
53 55
 echo "start-build: ok"
54 56
 
55 57
 oc cancel-build "${started}" --dump-logs --restart
58
+oc delete all --all
59
+oc process -f examples/sample-app/application-template-dockerbuild.json -l build=docker | oc create -f -
60
+tryuntil oc get build/ruby-sample-build-1
61
+# Uses type/name resource syntax
62
+oc cancel-build build/ruby-sample-build-1
63
+oc delete all --all
56 64
 echo "cancel-build: ok"
57
-
58
-[ "$(oc delete is/ruby-20-centos7-buildcli | grep 'imagestream "ruby-20-centos7-buildcli" deleted')" ]
59
-[ "$(oc delete bc/ruby-sample-build-validtag -o name | grep 'buildconfig/ruby-sample-build-validtag')" ]
60
-[ "$(oc delete bc/ruby-sample-build-invalidtag | grep 'buildconfig "ruby-sample-build-invalidtag" deleted')" ]
... ...
@@ -31,6 +31,7 @@ oc describe deploymentConfigs test-deployment-config
31 31
 [ "$(oc env dc/test-deployment-config TEST=bar OTHER=baz BAR-)" ]
32 32
 
33 33
 oc deploy test-deployment-config
34
+oc deploy dc/test-deployment-config
34 35
 oc delete deploymentConfigs test-deployment-config
35 36
 echo "deploymentConfigs: ok"
36 37
 
... ...
@@ -24,11 +24,11 @@ oc secrets new from-file .dockercfg=${HOME}/dockerconfig
24 24
 
25 25
 # attach secrets to service account
26 26
 # single secret with prefix
27
-oc secrets add serviceaccounts/deployer secrets/dockercfg
27
+oc secrets add deployer dockercfg
28 28
 # don't add the same secret twice
29
-oc secrets add serviceaccounts/deployer secrets/dockercfg secrets/from-file
29
+oc secrets add serviceaccounts/deployer dockercfg secrets/from-file
30 30
 # make sure we can add as as pull secret
31
-oc secrets add serviceaccounts/deployer secrets/dockercfg secrets/from-file --for=pull
31
+oc secrets add deployer dockercfg from-file --for=pull
32 32
 # make sure we can add as as pull secret and mount secret at once
33 33
 oc secrets add serviceaccounts/deployer secrets/dockercfg secrets/from-file --for=pull,mount
34 34