Browse code

set build-hook command

Cesar Wong authored on 2016/06/07 05:42:39
Showing 7 changed files
... ...
@@ -1677,6 +1677,73 @@ _oc_set_triggers()
1677 1677
     must_have_one_noun=()
1678 1678
 }
1679 1679
 
1680
+_oc_set_build-hook()
1681
+{
1682
+    last_command="oc_set_build-hook"
1683
+    commands=()
1684
+
1685
+    flags=()
1686
+    two_word_flags=()
1687
+    flags_with_completion=()
1688
+    flags_completion=()
1689
+
1690
+    flags+=("--all")
1691
+    flags+=("--command")
1692
+    flags+=("--filename=")
1693
+    flags_with_completion+=("--filename")
1694
+    flags_completion+=("__handle_filename_extension_flag yaml|yml|json")
1695
+    two_word_flags+=("-f")
1696
+    flags_with_completion+=("-f")
1697
+    flags_completion+=("__handle_filename_extension_flag yaml|yml|json")
1698
+    flags+=("--no-headers")
1699
+    flags+=("--output=")
1700
+    two_word_flags+=("-o")
1701
+    flags+=("--output-version=")
1702
+    flags+=("--post-commit")
1703
+    flags+=("--remove")
1704
+    flags+=("--script=")
1705
+    flags+=("--selector=")
1706
+    two_word_flags+=("-l")
1707
+    flags+=("--show-all")
1708
+    flags+=("-a")
1709
+    flags+=("--show-labels")
1710
+    flags+=("--sort-by=")
1711
+    flags+=("--template=")
1712
+    flags_with_completion+=("--template")
1713
+    flags_completion+=("_filedir")
1714
+    two_word_flags+=("-t")
1715
+    flags_with_completion+=("-t")
1716
+    flags_completion+=("_filedir")
1717
+    flags+=("--api-version=")
1718
+    flags+=("--as=")
1719
+    flags+=("--certificate-authority=")
1720
+    flags_with_completion+=("--certificate-authority")
1721
+    flags_completion+=("_filedir")
1722
+    flags+=("--client-certificate=")
1723
+    flags_with_completion+=("--client-certificate")
1724
+    flags_completion+=("_filedir")
1725
+    flags+=("--client-key=")
1726
+    flags_with_completion+=("--client-key")
1727
+    flags_completion+=("_filedir")
1728
+    flags+=("--cluster=")
1729
+    flags+=("--config=")
1730
+    flags_with_completion+=("--config")
1731
+    flags_completion+=("_filedir")
1732
+    flags+=("--context=")
1733
+    flags+=("--google-json-key=")
1734
+    flags+=("--insecure-skip-tls-verify")
1735
+    flags+=("--log-flush-frequency=")
1736
+    flags+=("--match-server-version")
1737
+    flags+=("--namespace=")
1738
+    two_word_flags+=("-n")
1739
+    flags+=("--server=")
1740
+    flags+=("--token=")
1741
+    flags+=("--user=")
1742
+
1743
+    must_have_one_flag=()
1744
+    must_have_one_noun=()
1745
+}
1746
+
1680 1747
 _oc_set()
1681 1748
 {
1682 1749
     last_command="oc_set"
... ...
@@ -1685,6 +1752,7 @@ _oc_set()
1685 1685
     commands+=("volumes")
1686 1686
     commands+=("probe")
1687 1687
     commands+=("triggers")
1688
+    commands+=("build-hook")
1688 1689
 
1689 1690
     flags=()
1690 1691
     two_word_flags=()
... ...
@@ -5265,6 +5265,73 @@ _openshift_cli_set_triggers()
5265 5265
     must_have_one_noun=()
5266 5266
 }
5267 5267
 
5268
+_openshift_cli_set_build-hook()
5269
+{
5270
+    last_command="openshift_cli_set_build-hook"
5271
+    commands=()
5272
+
5273
+    flags=()
5274
+    two_word_flags=()
5275
+    flags_with_completion=()
5276
+    flags_completion=()
5277
+
5278
+    flags+=("--all")
5279
+    flags+=("--command")
5280
+    flags+=("--filename=")
5281
+    flags_with_completion+=("--filename")
5282
+    flags_completion+=("__handle_filename_extension_flag yaml|yml|json")
5283
+    two_word_flags+=("-f")
5284
+    flags_with_completion+=("-f")
5285
+    flags_completion+=("__handle_filename_extension_flag yaml|yml|json")
5286
+    flags+=("--no-headers")
5287
+    flags+=("--output=")
5288
+    two_word_flags+=("-o")
5289
+    flags+=("--output-version=")
5290
+    flags+=("--post-commit")
5291
+    flags+=("--remove")
5292
+    flags+=("--script=")
5293
+    flags+=("--selector=")
5294
+    two_word_flags+=("-l")
5295
+    flags+=("--show-all")
5296
+    flags+=("-a")
5297
+    flags+=("--show-labels")
5298
+    flags+=("--sort-by=")
5299
+    flags+=("--template=")
5300
+    flags_with_completion+=("--template")
5301
+    flags_completion+=("_filedir")
5302
+    two_word_flags+=("-t")
5303
+    flags_with_completion+=("-t")
5304
+    flags_completion+=("_filedir")
5305
+    flags+=("--api-version=")
5306
+    flags+=("--as=")
5307
+    flags+=("--certificate-authority=")
5308
+    flags_with_completion+=("--certificate-authority")
5309
+    flags_completion+=("_filedir")
5310
+    flags+=("--client-certificate=")
5311
+    flags_with_completion+=("--client-certificate")
5312
+    flags_completion+=("_filedir")
5313
+    flags+=("--client-key=")
5314
+    flags_with_completion+=("--client-key")
5315
+    flags_completion+=("_filedir")
5316
+    flags+=("--cluster=")
5317
+    flags+=("--config=")
5318
+    flags_with_completion+=("--config")
5319
+    flags_completion+=("_filedir")
5320
+    flags+=("--context=")
5321
+    flags+=("--google-json-key=")
5322
+    flags+=("--insecure-skip-tls-verify")
5323
+    flags+=("--log-flush-frequency=")
5324
+    flags+=("--match-server-version")
5325
+    flags+=("--namespace=")
5326
+    two_word_flags+=("-n")
5327
+    flags+=("--server=")
5328
+    flags+=("--token=")
5329
+    flags+=("--user=")
5330
+
5331
+    must_have_one_flag=()
5332
+    must_have_one_noun=()
5333
+}
5334
+
5268 5335
 _openshift_cli_set()
5269 5336
 {
5270 5337
     last_command="openshift_cli_set"
... ...
@@ -5273,6 +5340,7 @@ _openshift_cli_set()
5273 5273
     commands+=("volumes")
5274 5274
     commands+=("probe")
5275 5275
     commands+=("triggers")
5276
+    commands+=("build-hook")
5276 5277
 
5277 5278
     flags=()
5278 5279
     two_word_flags=()
... ...
@@ -1901,6 +1901,28 @@ Generate a new token for a service account.
1901 1901
 ====
1902 1902
 
1903 1903
 
1904
+== oc set build-hook
1905
+Update a build hook on a build config
1906
+
1907
+====
1908
+
1909
+[options="nowrap"]
1910
+----
1911
+  # Clear post-commit hook on a build config
1912
+  oc set build-hook bc/mybuild --post-commit --remove
1913
+
1914
+  # Set the post-commit hook to execute a test suite using a new entrypoint
1915
+  oc set build-hook bc/mybuild --post-commit --command -- /bin/bash -c /var/lib/test-image.sh
1916
+
1917
+  # Set the post-commit hook to execute a shell script
1918
+  oc set build-hook bc/mybuild --post-commit --script="/var/lib/test-image.sh param1 param2 && /var/lib/done.sh"
1919
+
1920
+  # Set the post-commit hook as a set of arguments to the default image entrypoint
1921
+  oc set build-hook bc/mybuild --post-commit  -- arg1 arg2
1922
+----
1923
+====
1924
+
1925
+
1904 1926
 == oc set env
1905 1927
 Update environment variables on a pod template
1906 1928
 
1907 1929
new file mode 100644
... ...
@@ -0,0 +1,278 @@
0
+package set
1
+
2
+import (
3
+	"fmt"
4
+	"io"
5
+	"os"
6
+
7
+	"github.com/spf13/cobra"
8
+	kapi "k8s.io/kubernetes/pkg/api"
9
+	"k8s.io/kubernetes/pkg/api/meta"
10
+	"k8s.io/kubernetes/pkg/api/unversioned"
11
+	kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
12
+	"k8s.io/kubernetes/pkg/kubectl/resource"
13
+	"k8s.io/kubernetes/pkg/runtime"
14
+
15
+	buildapi "github.com/openshift/origin/pkg/build/api"
16
+	cmdutil "github.com/openshift/origin/pkg/cmd/util"
17
+	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
18
+)
19
+
20
+const (
21
+	buildHookLong = `
22
+Set or remove a build hook on a build config
23
+
24
+Build hooks allow behavior to be injected into the build process.
25
+
26
+A post-commit build hook is executed after a build has committed an image but before the
27
+image has been pushed to a registry. It can be used to execute tests on the image and verify
28
+it before it is made available in a registry or for any other logic that is needed to execute
29
+before the image is pushed to the registry. A new container with the recently built image is
30
+launched with the build hook command. If the command or script run by the build hook returns a
31
+non-zero exit code, the resulting image will not be pushed to the registry.
32
+
33
+The command for a build hook may be specified as a shell script (with the --script argument),
34
+as a new entrypoint command on the image with the --command argument, or as a set of
35
+arguments to the image's entrypoint (default).
36
+`
37
+
38
+	buildHookExample = `  # Clear post-commit hook on a build config
39
+  %[1]s build-hook bc/mybuild --post-commit --remove
40
+
41
+  # Set the post-commit hook to execute a test suite using a new entrypoint
42
+  %[1]s build-hook bc/mybuild --post-commit --command -- /bin/bash -c /var/lib/test-image.sh
43
+
44
+  # Set the post-commit hook to execute a shell script
45
+  %[1]s build-hook bc/mybuild --post-commit --script="/var/lib/test-image.sh param1 param2 && /var/lib/done.sh"
46
+
47
+  # Set the post-commit hook as a set of arguments to the default image entrypoint
48
+  %[1]s build-hook bc/mybuild --post-commit  -- arg1 arg2`
49
+)
50
+
51
+type BuildHookOptions struct {
52
+	Out io.Writer
53
+	Err io.Writer
54
+
55
+	Builder *resource.Builder
56
+	Infos   []*resource.Info
57
+
58
+	Encoder       runtime.Encoder
59
+	OutputVersion unversioned.GroupVersion
60
+
61
+	Filenames []string
62
+	Selector  string
63
+	All       bool
64
+
65
+	ShortOutput bool
66
+	Mapper      meta.RESTMapper
67
+
68
+	PrintObject func(runtime.Object) error
69
+
70
+	Script     string
71
+	Entrypoint bool
72
+	Remove     bool
73
+	PostCommit bool
74
+
75
+	Command []string
76
+}
77
+
78
+// NewCmdBuildHook implements the set build-hook command
79
+func NewCmdBuildHook(fullName string, f *clientcmd.Factory, out, errOut io.Writer) *cobra.Command {
80
+	options := &BuildHookOptions{
81
+		Out: out,
82
+		Err: errOut,
83
+	}
84
+	cmd := &cobra.Command{
85
+		Use:     "build-hook BUILDCONFIG --post-commit [--command] [--script] -- CMD",
86
+		Short:   "Update a build hook on a build config",
87
+		Long:    buildHookLong,
88
+		Example: fmt.Sprintf(buildHookExample, fullName),
89
+		Run: func(cmd *cobra.Command, args []string) {
90
+			kcmdutil.CheckErr(options.Complete(f, cmd, args))
91
+			kcmdutil.CheckErr(options.Validate())
92
+			if err := options.Run(); err != nil {
93
+				// TODO: move met to kcmdutil
94
+				if err == cmdutil.ErrExit {
95
+					os.Exit(1)
96
+				}
97
+				kcmdutil.CheckErr(err)
98
+			}
99
+		},
100
+	}
101
+
102
+	kcmdutil.AddPrinterFlags(cmd)
103
+	cmd.Flags().StringVarP(&options.Selector, "selector", "l", options.Selector, "Selector (label query) to filter build configs")
104
+	cmd.Flags().BoolVar(&options.All, "all", options.All, "Select all build configs in the namespace")
105
+	cmd.Flags().StringSliceVarP(&options.Filenames, "filename", "f", options.Filenames, "Filename, directory, or URL to file to use to edit the resource.")
106
+
107
+	cmd.Flags().BoolVar(&options.PostCommit, "post-commit", options.PostCommit, "If true, set the post-commit build hook on a build config")
108
+	cmd.Flags().BoolVar(&options.Entrypoint, "command", options.Entrypoint, "If true, set the entrypoint of the hook container to the given command")
109
+	cmd.Flags().StringVar(&options.Script, "script", options.Script, "Specify a script to run for the build-hook")
110
+	cmd.Flags().BoolVar(&options.Remove, "remove", options.Remove, "If true, remove the build hook.")
111
+
112
+	cmd.MarkFlagFilename("filename", "yaml", "yml", "json")
113
+
114
+	return cmd
115
+}
116
+
117
+func (o *BuildHookOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, args []string) error {
118
+	resources := args
119
+	if i := cmd.ArgsLenAtDash(); i != -1 {
120
+		resources = args[:i]
121
+		o.Command = args[i:]
122
+	}
123
+	if len(o.Filenames) == 0 && len(args) < 1 {
124
+		return kcmdutil.UsageError(cmd, "one or more build configs must be specified as <name> or <resource>/<name>")
125
+	}
126
+
127
+	cmdNamespace, explicit, err := f.DefaultNamespace()
128
+	if err != nil {
129
+		return err
130
+	}
131
+	clientConfig, err := f.ClientConfig()
132
+	if err != nil {
133
+		return err
134
+	}
135
+	o.OutputVersion, err = kcmdutil.OutputVersion(cmd, clientConfig.GroupVersion)
136
+	if err != nil {
137
+		return err
138
+	}
139
+
140
+	mapper, typer := f.Object(false)
141
+	o.Builder = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), kapi.Codecs.UniversalDecoder()).
142
+		ContinueOnError().
143
+		NamespaceParam(cmdNamespace).DefaultNamespace().
144
+		FilenameParam(explicit, false, o.Filenames...).
145
+		SelectorParam(o.Selector).
146
+		ResourceNames("buildconfigs", resources...).
147
+		Flatten()
148
+
149
+	if o.All {
150
+		o.Builder.ResourceTypes("buildconfigs").SelectAllParam(o.All)
151
+	}
152
+
153
+	output := kcmdutil.GetFlagString(cmd, "output")
154
+	if len(output) != 0 {
155
+		o.PrintObject = func(obj runtime.Object) error { return f.PrintObject(cmd, mapper, obj, o.Out) }
156
+	}
157
+
158
+	o.Encoder = f.JSONEncoder()
159
+	o.ShortOutput = kcmdutil.GetFlagString(cmd, "output") == "name"
160
+	o.Mapper = mapper
161
+
162
+	return nil
163
+}
164
+
165
+func (o *BuildHookOptions) Validate() error {
166
+
167
+	if !o.PostCommit {
168
+		return fmt.Errorf("you must specify a type of hook to set")
169
+	}
170
+
171
+	if o.Remove {
172
+		if len(o.Command) > 0 {
173
+			return fmt.Errorf("--remove may not be used with any other option")
174
+		}
175
+		return nil
176
+	}
177
+
178
+	if len(o.Script) > 0 && o.Entrypoint {
179
+		return fmt.Errorf("--script and --command cannot be specified together")
180
+	}
181
+
182
+	if len(o.Script) > 0 && len(o.Command) > 0 {
183
+		return fmt.Errorf("a command cannot be specified when using the --script argument")
184
+	}
185
+
186
+	if len(o.Command) == 0 && len(o.Script) == 0 {
187
+		return fmt.Errorf("you must specify either a script or command for the build hook")
188
+	}
189
+	return nil
190
+}
191
+
192
+func (o *BuildHookOptions) Run() error {
193
+	infos := o.Infos
194
+	singular := len(o.Infos) <= 1
195
+	if o.Builder != nil {
196
+		loaded, err := o.Builder.Do().IntoSingular(&singular).Infos()
197
+		if err != nil {
198
+			return err
199
+		}
200
+		infos = loaded
201
+	}
202
+
203
+	patches := CalculatePatches(infos, o.Encoder, func(info *resource.Info) (bool, error) {
204
+		bc, ok := info.Object.(*buildapi.BuildConfig)
205
+		if !ok {
206
+			return false, nil
207
+		}
208
+		o.updateBuildConfig(bc)
209
+		return true, nil
210
+	})
211
+
212
+	if singular && len(patches) == 0 {
213
+		return fmt.Errorf("%s/%s is not a build config", infos[0].Mapping.Resource, infos[0].Name)
214
+	}
215
+
216
+	if o.PrintObject != nil {
217
+		object, err := resource.AsVersionedObject(infos, !singular, o.OutputVersion.String(), kapi.Codecs.LegacyCodec(o.OutputVersion))
218
+		if err != nil {
219
+			return err
220
+		}
221
+		return o.PrintObject(object)
222
+	}
223
+
224
+	failed := false
225
+	for _, patch := range patches {
226
+		info := patch.Info
227
+		if patch.Err != nil {
228
+			fmt.Fprintf(o.Err, "error: %s/%s %v\n", info.Mapping.Resource, info.Name, patch.Err)
229
+			continue
230
+		}
231
+
232
+		if string(patch.Patch) == "{}" || len(patch.Patch) == 0 {
233
+			fmt.Fprintf(o.Err, "info: %s %q was not changed\n", info.Mapping.Resource, info.Name)
234
+			continue
235
+		}
236
+
237
+		obj, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, kapi.StrategicMergePatchType, patch.Patch)
238
+		if err != nil {
239
+			fmt.Fprintf(o.Err, "error: %v\n", err)
240
+			failed = true
241
+			continue
242
+		}
243
+
244
+		info.Refresh(obj, true)
245
+		kcmdutil.PrintSuccess(o.Mapper, o.ShortOutput, o.Out, info.Mapping.Resource, info.Name, "updated")
246
+	}
247
+	if failed {
248
+		return cmdutil.ErrExit
249
+	}
250
+	return nil
251
+}
252
+
253
+func (o *BuildHookOptions) updateBuildConfig(bc *buildapi.BuildConfig) {
254
+	if o.Remove {
255
+		bc.Spec.PostCommit.Args = nil
256
+		bc.Spec.PostCommit.Command = nil
257
+		bc.Spec.PostCommit.Script = ""
258
+		return
259
+	}
260
+
261
+	switch {
262
+	case len(o.Script) > 0:
263
+		bc.Spec.PostCommit.Args = nil
264
+		bc.Spec.PostCommit.Command = nil
265
+		bc.Spec.PostCommit.Script = o.Script
266
+	case o.Entrypoint:
267
+		bc.Spec.PostCommit.Command = o.Command[0:1]
268
+		if len(o.Command) > 1 {
269
+			bc.Spec.PostCommit.Args = o.Command[1:]
270
+		}
271
+		bc.Spec.PostCommit.Script = ""
272
+	default:
273
+		bc.Spec.PostCommit.Command = nil
274
+		bc.Spec.PostCommit.Args = o.Command
275
+		bc.Spec.PostCommit.Script = ""
276
+	}
277
+}
... ...
@@ -42,6 +42,7 @@ func NewCmdSet(fullName string, f *clientcmd.Factory, in io.Reader, out, errout
42 42
 			Message: "Manage application flows:",
43 43
 			Commands: []*cobra.Command{
44 44
 				NewCmdTriggers(name, f, out, errout),
45
+				NewCmdBuildHook(name, f, out, errout),
45 46
 			},
46 47
 		},
47 48
 	}
... ...
@@ -128,6 +128,33 @@ os::cmd::expect_success 'oc delete all -l build=docker'
128 128
 echo "buildConfig: ok"
129 129
 os::test::junit::declare_suite_end
130 130
 
131
+os::test::junit::declare_suite_start "cmd/builds/setbuildhook"
132
+# Validate the set build-hook command
133
+arg="-f test/fixtures/test-bc.yaml"
134
+os::cmd::expect_failure_and_text "oc set build-hook" "error: one or more build configs"
135
+os::cmd::expect_failure_and_text "oc set build-hook ${arg}" "error: you must specify a type of hook"
136
+os::cmd::expect_success_and_text "oc set build-hook ${arg} --post-commit -o yaml -- echo 'hello world'" 'postCommit:'
137
+os::cmd::expect_success_and_text "oc set build-hook ${arg} --post-commit -o yaml -- echo 'hello world'" 'args:'
138
+os::cmd::expect_success_and_text "oc set build-hook ${arg} --post-commit -o yaml -- echo 'hello world'" '\- echo'
139
+os::cmd::expect_success_and_text "oc set build-hook ${arg} --post-commit -o yaml -- echo 'hello world'" '\- hello world'
140
+os::cmd::expect_success_and_not_text "oc set build-hook ${arg} --post-commit -o yaml -- echo 'hello world'" 'command:'
141
+os::cmd::expect_success_and_text "oc set build-hook ${arg} --post-commit --command -o yaml -- echo 'hello world'" 'command:'
142
+os::cmd::expect_success_and_text "oc set build-hook ${arg} --post-commit -o yaml --script='echo \"hello world\"'" 'script: echo \"hello world\"'
143
+# Server object tests
144
+os::cmd::expect_success "oc create -f test/fixtures/test-bc.yaml"
145
+os::cmd::expect_failure_and_text "oc set build-hook bc/test-buildconfig --post-commit" "you must specify either a script or command"
146
+os::cmd::expect_success_and_text "oc set build-hook test-buildconfig --post-commit -- echo 'hello world'" "updated"
147
+os::cmd::expect_success_and_text "oc set build-hook bc/test-buildconfig --post-commit -- echo 'hello world'" "was not changed"
148
+os::cmd::expect_success_and_text "oc get bc/test-buildconfig -o yaml" "args:"
149
+os::cmd::expect_success_and_text "oc set build-hook bc/test-buildconfig --post-commit --command -- /bin/bash -c \"echo 'test'\"" "updated"
150
+os::cmd::expect_success_and_text "oc get bc/test-buildconfig -o yaml" "command:"
151
+os::cmd::expect_success_and_text "oc set build-hook --all --post-commit -- echo 'all bc'" "updated"
152
+os::cmd::expect_success_and_text "oc get bc -o yaml" "all bc"
153
+os::cmd::expect_success_and_text "oc set build-hook bc/test-buildconfig --post-commit --remove" "updated"
154
+os::cmd::expect_success_and_not_text "oc get bc/test-buildconfig -o yaml" "args:"
155
+os::cmd::expect_success "oc delete bc/test-buildconfig"
156
+echo "set build-hook: ok"
157
+os::test::junit::declare_suite_end
131 158
 
132 159
 os::test::junit::declare_suite_start "cmd/builds/start-build"
133 160
 os::cmd::expect_success 'oc create -f test/integration/fixtures/test-buildcli.json'
134 161
new file mode 100644
... ...
@@ -0,0 +1,21 @@
0
+apiVersion: v1
1
+kind: BuildConfig
2
+metadata:
3
+  name: test-buildconfig
4
+spec:
5
+  output: {}
6
+  runPolicy: Serial
7
+  source:
8
+    git:
9
+      uri: git://github.com/openshift/ruby-hello-world.git
10
+    secrets: null
11
+    type: Git
12
+  strategy:
13
+    sourceStrategy:
14
+      from:
15
+        kind: DockerImage
16
+        name: centos/ruby-22-centos7
17
+    type: Source
18
+  triggers: []
19
+status:
20
+  lastVersion: 0