`oc set triggers` will display, remove, update, or alter triggers on
deployment configs and build configs.
... | ... |
@@ -1412,6 +1412,74 @@ _oc_set_probe() |
1412 | 1412 |
must_have_one_noun=() |
1413 | 1413 |
} |
1414 | 1414 |
|
1415 |
+_oc_set_triggers() |
|
1416 |
+{ |
|
1417 |
+ last_command="oc_set_triggers" |
|
1418 |
+ commands=() |
|
1419 |
+ |
|
1420 |
+ flags=() |
|
1421 |
+ two_word_flags=() |
|
1422 |
+ flags_with_completion=() |
|
1423 |
+ flags_completion=() |
|
1424 |
+ |
|
1425 |
+ flags+=("--all") |
|
1426 |
+ flags+=("--auto") |
|
1427 |
+ flags+=("--containers=") |
|
1428 |
+ two_word_flags+=("-c") |
|
1429 |
+ flags+=("--filename=") |
|
1430 |
+ flags_with_completion+=("--filename") |
|
1431 |
+ flags_completion+=("__handle_filename_extension_flag yaml|yml|json") |
|
1432 |
+ two_word_flags+=("-f") |
|
1433 |
+ flags_with_completion+=("-f") |
|
1434 |
+ flags_completion+=("__handle_filename_extension_flag yaml|yml|json") |
|
1435 |
+ flags+=("--from-config") |
|
1436 |
+ flags+=("--from-github") |
|
1437 |
+ flags+=("--from-image=") |
|
1438 |
+ flags+=("--from-webhook") |
|
1439 |
+ flags+=("--manual") |
|
1440 |
+ flags+=("--no-headers") |
|
1441 |
+ flags+=("--output=") |
|
1442 |
+ two_word_flags+=("-o") |
|
1443 |
+ flags+=("--output-version=") |
|
1444 |
+ flags+=("--remove") |
|
1445 |
+ flags+=("--remove-all") |
|
1446 |
+ flags+=("--selector=") |
|
1447 |
+ two_word_flags+=("-l") |
|
1448 |
+ flags+=("--show-all") |
|
1449 |
+ flags+=("-a") |
|
1450 |
+ flags+=("--show-labels") |
|
1451 |
+ flags+=("--sort-by=") |
|
1452 |
+ flags+=("--template=") |
|
1453 |
+ two_word_flags+=("-t") |
|
1454 |
+ flags+=("--api-version=") |
|
1455 |
+ flags+=("--certificate-authority=") |
|
1456 |
+ flags_with_completion+=("--certificate-authority") |
|
1457 |
+ flags_completion+=("_filedir") |
|
1458 |
+ flags+=("--client-certificate=") |
|
1459 |
+ flags_with_completion+=("--client-certificate") |
|
1460 |
+ flags_completion+=("_filedir") |
|
1461 |
+ flags+=("--client-key=") |
|
1462 |
+ flags_with_completion+=("--client-key") |
|
1463 |
+ flags_completion+=("_filedir") |
|
1464 |
+ flags+=("--cluster=") |
|
1465 |
+ flags+=("--config=") |
|
1466 |
+ flags_with_completion+=("--config") |
|
1467 |
+ flags_completion+=("_filedir") |
|
1468 |
+ flags+=("--context=") |
|
1469 |
+ flags+=("--google-json-key=") |
|
1470 |
+ flags+=("--insecure-skip-tls-verify") |
|
1471 |
+ flags+=("--log-flush-frequency=") |
|
1472 |
+ flags+=("--match-server-version") |
|
1473 |
+ flags+=("--namespace=") |
|
1474 |
+ two_word_flags+=("-n") |
|
1475 |
+ flags+=("--server=") |
|
1476 |
+ flags+=("--token=") |
|
1477 |
+ flags+=("--user=") |
|
1478 |
+ |
|
1479 |
+ must_have_one_flag=() |
|
1480 |
+ must_have_one_noun=() |
|
1481 |
+} |
|
1482 |
+ |
|
1415 | 1483 |
_oc_set() |
1416 | 1484 |
{ |
1417 | 1485 |
last_command="oc_set" |
... | ... |
@@ -1419,6 +1487,7 @@ _oc_set() |
1419 | 1419 |
commands+=("env") |
1420 | 1420 |
commands+=("volumes") |
1421 | 1421 |
commands+=("probe") |
1422 |
+ commands+=("triggers") |
|
1422 | 1423 |
|
1423 | 1424 |
flags=() |
1424 | 1425 |
two_word_flags=() |
... | ... |
@@ -4749,6 +4749,74 @@ _openshift_cli_set_probe() |
4749 | 4749 |
must_have_one_noun=() |
4750 | 4750 |
} |
4751 | 4751 |
|
4752 |
+_openshift_cli_set_triggers() |
|
4753 |
+{ |
|
4754 |
+ last_command="openshift_cli_set_triggers" |
|
4755 |
+ commands=() |
|
4756 |
+ |
|
4757 |
+ flags=() |
|
4758 |
+ two_word_flags=() |
|
4759 |
+ flags_with_completion=() |
|
4760 |
+ flags_completion=() |
|
4761 |
+ |
|
4762 |
+ flags+=("--all") |
|
4763 |
+ flags+=("--auto") |
|
4764 |
+ flags+=("--containers=") |
|
4765 |
+ two_word_flags+=("-c") |
|
4766 |
+ flags+=("--filename=") |
|
4767 |
+ flags_with_completion+=("--filename") |
|
4768 |
+ flags_completion+=("__handle_filename_extension_flag yaml|yml|json") |
|
4769 |
+ two_word_flags+=("-f") |
|
4770 |
+ flags_with_completion+=("-f") |
|
4771 |
+ flags_completion+=("__handle_filename_extension_flag yaml|yml|json") |
|
4772 |
+ flags+=("--from-config") |
|
4773 |
+ flags+=("--from-github") |
|
4774 |
+ flags+=("--from-image=") |
|
4775 |
+ flags+=("--from-webhook") |
|
4776 |
+ flags+=("--manual") |
|
4777 |
+ flags+=("--no-headers") |
|
4778 |
+ flags+=("--output=") |
|
4779 |
+ two_word_flags+=("-o") |
|
4780 |
+ flags+=("--output-version=") |
|
4781 |
+ flags+=("--remove") |
|
4782 |
+ flags+=("--remove-all") |
|
4783 |
+ flags+=("--selector=") |
|
4784 |
+ two_word_flags+=("-l") |
|
4785 |
+ flags+=("--show-all") |
|
4786 |
+ flags+=("-a") |
|
4787 |
+ flags+=("--show-labels") |
|
4788 |
+ flags+=("--sort-by=") |
|
4789 |
+ flags+=("--template=") |
|
4790 |
+ two_word_flags+=("-t") |
|
4791 |
+ flags+=("--api-version=") |
|
4792 |
+ flags+=("--certificate-authority=") |
|
4793 |
+ flags_with_completion+=("--certificate-authority") |
|
4794 |
+ flags_completion+=("_filedir") |
|
4795 |
+ flags+=("--client-certificate=") |
|
4796 |
+ flags_with_completion+=("--client-certificate") |
|
4797 |
+ flags_completion+=("_filedir") |
|
4798 |
+ flags+=("--client-key=") |
|
4799 |
+ flags_with_completion+=("--client-key") |
|
4800 |
+ flags_completion+=("_filedir") |
|
4801 |
+ flags+=("--cluster=") |
|
4802 |
+ flags+=("--config=") |
|
4803 |
+ flags_with_completion+=("--config") |
|
4804 |
+ flags_completion+=("_filedir") |
|
4805 |
+ flags+=("--context=") |
|
4806 |
+ flags+=("--google-json-key=") |
|
4807 |
+ flags+=("--insecure-skip-tls-verify") |
|
4808 |
+ flags+=("--log-flush-frequency=") |
|
4809 |
+ flags+=("--match-server-version") |
|
4810 |
+ flags+=("--namespace=") |
|
4811 |
+ two_word_flags+=("-n") |
|
4812 |
+ flags+=("--server=") |
|
4813 |
+ flags+=("--token=") |
|
4814 |
+ flags+=("--user=") |
|
4815 |
+ |
|
4816 |
+ must_have_one_flag=() |
|
4817 |
+ must_have_one_noun=() |
|
4818 |
+} |
|
4819 |
+ |
|
4752 | 4820 |
_openshift_cli_set() |
4753 | 4821 |
{ |
4754 | 4822 |
last_command="openshift_cli_set" |
... | ... |
@@ -4756,6 +4824,7 @@ _openshift_cli_set() |
4756 | 4756 |
commands+=("env") |
4757 | 4757 |
commands+=("volumes") |
4758 | 4758 |
commands+=("probe") |
4759 |
+ commands+=("triggers") |
|
4759 | 4760 |
|
4760 | 4761 |
flags=() |
4761 | 4762 |
two_word_flags=() |
... | ... |
@@ -1688,6 +1688,38 @@ Update a probe on a pod template |
1688 | 1688 |
==== |
1689 | 1689 |
|
1690 | 1690 |
|
1691 |
+== oc set triggers |
|
1692 |
+Update the triggers on a build or deployment config |
|
1693 |
+ |
|
1694 |
+==== |
|
1695 |
+ |
|
1696 |
+[options="nowrap"] |
|
1697 |
+---- |
|
1698 |
+ # Print the triggers on the registry |
|
1699 |
+ $ oc set triggers dc/registry |
|
1700 |
+ |
|
1701 |
+ # Set all triggers to manual |
|
1702 |
+ $ oc set triggers dc/registry --manual |
|
1703 |
+ |
|
1704 |
+ # Enable all automatic triggers |
|
1705 |
+ $ oc set triggers dc/registry --auto |
|
1706 |
+ |
|
1707 |
+ # Reset the GitHub webhook on a build to a new, generated secret |
|
1708 |
+ $ oc set triggers bc/webapp --from-github= |
|
1709 |
+ $ oc set triggers bc/webapp --from-webhook= |
|
1710 |
+ |
|
1711 |
+ # Remove all triggers |
|
1712 |
+ $ oc set triggers bc/webapp --remove-all |
|
1713 |
+ |
|
1714 |
+ # Stop triggering on config change |
|
1715 |
+ $ oc set triggers dc/registry --from-config --remove |
|
1716 |
+ |
|
1717 |
+ # Add an image trigger to a build config |
|
1718 |
+ $ oc set triggers bc/webapp --from-image=namespace1/image:latest |
|
1719 |
+---- |
|
1720 |
+==== |
|
1721 |
+ |
|
1722 |
+ |
|
1691 | 1723 |
== oc set volumes |
1692 | 1724 |
Update volumes on a pod template |
1693 | 1725 |
|
... | ... |
@@ -38,6 +38,12 @@ func NewCmdSet(fullName string, f *clientcmd.Factory, in io.Reader, out, errout |
38 | 38 |
NewCmdProbe(name, f, out, errout), |
39 | 39 |
}, |
40 | 40 |
}, |
41 |
+ { |
|
42 |
+ Message: "Manage application flows:", |
|
43 |
+ Commands: []*cobra.Command{ |
|
44 |
+ NewCmdTriggers(name, f, out, errout), |
|
45 |
+ }, |
|
46 |
+ }, |
|
41 | 47 |
} |
42 | 48 |
groups.Add(set) |
43 | 49 |
templates.ActsAsRootCommand(set, []string{"options"}, groups...) |
44 | 50 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,770 @@ |
0 |
+package set |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "fmt" |
|
4 |
+ "io" |
|
5 |
+ "os" |
|
6 |
+ "reflect" |
|
7 |
+ "strings" |
|
8 |
+ "text/tabwriter" |
|
9 |
+ |
|
10 |
+ "github.com/golang/glog" |
|
11 |
+ "github.com/spf13/cobra" |
|
12 |
+ |
|
13 |
+ kapi "k8s.io/kubernetes/pkg/api" |
|
14 |
+ "k8s.io/kubernetes/pkg/api/meta" |
|
15 |
+ kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" |
|
16 |
+ "k8s.io/kubernetes/pkg/kubectl/resource" |
|
17 |
+ "k8s.io/kubernetes/pkg/runtime" |
|
18 |
+ |
|
19 |
+ buildapi "github.com/openshift/origin/pkg/build/api" |
|
20 |
+ buildutil "github.com/openshift/origin/pkg/build/util" |
|
21 |
+ cmdutil "github.com/openshift/origin/pkg/cmd/util" |
|
22 |
+ "github.com/openshift/origin/pkg/cmd/util/clientcmd" |
|
23 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
24 |
+ "github.com/openshift/origin/pkg/generate/app" |
|
25 |
+ imageapi "github.com/openshift/origin/pkg/image/api" |
|
26 |
+ "k8s.io/kubernetes/pkg/util/sets" |
|
27 |
+) |
|
28 |
+ |
|
29 |
+const ( |
|
30 |
+ triggersLong = ` |
|
31 |
+Set or remove triggers for build configs and deployment configs |
|
32 |
+ |
|
33 |
+All build configs and deployment configs may have a set of triggers that result in a new deployment |
|
34 |
+or build being created. This command enables you to alter those triggers - making them automatic or |
|
35 |
+manual, adding new entries, or changing existing entries. |
|
36 |
+ |
|
37 |
+Deployments support triggering off of image changes and on config changes. Config changes are any |
|
38 |
+alterations to the pod template, while image changes will result in the container image value being |
|
39 |
+updated whenever an image stream tag is updated. |
|
40 |
+ |
|
41 |
+Build configs support triggering off of image changes, config changes, and webhooks (both GitHub-specific |
|
42 |
+and generic). The config change trigger for a build config will only trigger the first build.` |
|
43 |
+ |
|
44 |
+ triggersExample = ` # Print the triggers on the registry |
|
45 |
+ $ %[1]s triggers dc/registry |
|
46 |
+ |
|
47 |
+ # Set all triggers to manual |
|
48 |
+ $ %[1]s triggers dc/registry --manual |
|
49 |
+ |
|
50 |
+ # Enable all automatic triggers |
|
51 |
+ $ %[1]s triggers dc/registry --auto |
|
52 |
+ |
|
53 |
+ # Reset the GitHub webhook on a build to a new, generated secret |
|
54 |
+ $ %[1]s triggers bc/webapp --from-github= |
|
55 |
+ $ %[1]s triggers bc/webapp --from-webhook= |
|
56 |
+ |
|
57 |
+ # Remove all triggers |
|
58 |
+ $ %[1]s triggers bc/webapp --remove-all |
|
59 |
+ |
|
60 |
+ # Stop triggering on config change |
|
61 |
+ $ %[1]s triggers dc/registry --from-config --remove |
|
62 |
+ |
|
63 |
+ # Add an image trigger to a build config |
|
64 |
+ $ %[1]s triggers bc/webapp --from-image=namespace1/image:latest` |
|
65 |
+) |
|
66 |
+ |
|
67 |
+type TriggersOptions struct { |
|
68 |
+ Out io.Writer |
|
69 |
+ Err io.Writer |
|
70 |
+ |
|
71 |
+ Filenames []string |
|
72 |
+ Selector string |
|
73 |
+ All bool |
|
74 |
+ |
|
75 |
+ Builder *resource.Builder |
|
76 |
+ Infos []*resource.Info |
|
77 |
+ |
|
78 |
+ Encoder runtime.Encoder |
|
79 |
+ |
|
80 |
+ ShortOutput bool |
|
81 |
+ Mapper meta.RESTMapper |
|
82 |
+ |
|
83 |
+ PrintTable bool |
|
84 |
+ PrintObject func(runtime.Object) error |
|
85 |
+ |
|
86 |
+ Remove bool |
|
87 |
+ RemoveAll bool |
|
88 |
+ Auto bool |
|
89 |
+ Manual bool |
|
90 |
+ Reset bool |
|
91 |
+ |
|
92 |
+ ContainerNames string |
|
93 |
+ FromConfig bool |
|
94 |
+ FromGitHub *bool |
|
95 |
+ FromWebHook *bool |
|
96 |
+ FromImage string |
|
97 |
+ // FromImageNamespace is the namespace for the FromImage |
|
98 |
+ FromImageNamespace string |
|
99 |
+} |
|
100 |
+ |
|
101 |
+// NewCmdTriggers implements the set triggers command |
|
102 |
+func NewCmdTriggers(fullName string, f *clientcmd.Factory, out, errOut io.Writer) *cobra.Command { |
|
103 |
+ options := &TriggersOptions{ |
|
104 |
+ Out: out, |
|
105 |
+ Err: errOut, |
|
106 |
+ } |
|
107 |
+ cmd := &cobra.Command{ |
|
108 |
+ Use: "triggers RESOURCE/NAME [--from-config|--from-image|--from-github|--from-webhook] [--auto|--manual]", |
|
109 |
+ Short: "Update the triggers on a build or deployment config", |
|
110 |
+ Long: triggersLong, |
|
111 |
+ Example: fmt.Sprintf(triggersExample, fullName), |
|
112 |
+ Run: func(cmd *cobra.Command, args []string) { |
|
113 |
+ kcmdutil.CheckErr(options.Complete(f, cmd, args)) |
|
114 |
+ kcmdutil.CheckErr(options.Validate()) |
|
115 |
+ if err := options.Run(); err != nil { |
|
116 |
+ // TODO: move met to kcmdutil |
|
117 |
+ if err == cmdutil.ErrExit { |
|
118 |
+ os.Exit(1) |
|
119 |
+ } |
|
120 |
+ kcmdutil.CheckErr(err) |
|
121 |
+ } |
|
122 |
+ }, |
|
123 |
+ } |
|
124 |
+ |
|
125 |
+ kcmdutil.AddPrinterFlags(cmd) |
|
126 |
+ cmd.Flags().StringVarP(&options.Selector, "selector", "l", options.Selector, "Selector (label query) to filter on") |
|
127 |
+ cmd.Flags().BoolVar(&options.All, "all", options.All, "Select all resources in the namespace of the specified resource types") |
|
128 |
+ cmd.Flags().StringSliceVarP(&options.Filenames, "filename", "f", options.Filenames, "Filename, directory, or URL to file to use to edit the resource.") |
|
129 |
+ |
|
130 |
+ cmd.Flags().BoolVar(&options.Remove, "remove", options.Remove, "If true, remove the specified trigger(s).") |
|
131 |
+ cmd.Flags().BoolVar(&options.RemoveAll, "remove-all", options.RemoveAll, "If true, remove all triggers.") |
|
132 |
+ cmd.Flags().BoolVar(&options.Auto, "auto", options.Auto, "Enable all triggers, or just the specified trigger") |
|
133 |
+ cmd.Flags().BoolVar(&options.Manual, "manual", options.Manual, "Set all triggers to manual, or just the specified trigger") |
|
134 |
+ |
|
135 |
+ cmd.Flags().BoolVar(&options.FromConfig, "from-config", options.FromConfig, "If set, configuration changes will result in a change") |
|
136 |
+ cmd.Flags().StringVarP(&options.ContainerNames, "containers", "c", options.ContainerNames, "Comma delimited list of container names this trigger applies to on deployments; defaults to the name of the only container") |
|
137 |
+ cmd.Flags().StringVar(&options.FromImage, "from-image", options.FromImage, "An image stream tag to trigger off of") |
|
138 |
+ options.FromGitHub = cmd.Flags().Bool("from-github", false, "A GitHub webhook - a secret value will be generated automatically") |
|
139 |
+ options.FromWebHook = cmd.Flags().Bool("from-webhook", false, "A generic webhook - a secret value will be generated automatically") |
|
140 |
+ |
|
141 |
+ cmd.MarkFlagFilename("filename", "yaml", "yml", "json") |
|
142 |
+ |
|
143 |
+ return cmd |
|
144 |
+} |
|
145 |
+ |
|
146 |
+func (o *TriggersOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, args []string) error { |
|
147 |
+ cmdNamespace, explicit, err := f.DefaultNamespace() |
|
148 |
+ if err != nil { |
|
149 |
+ return err |
|
150 |
+ } |
|
151 |
+ |
|
152 |
+ if !cmd.Flags().Lookup("from-github").Changed { |
|
153 |
+ o.FromGitHub = nil |
|
154 |
+ } |
|
155 |
+ if !cmd.Flags().Lookup("from-webhook").Changed { |
|
156 |
+ o.FromWebHook = nil |
|
157 |
+ } |
|
158 |
+ |
|
159 |
+ if len(o.FromImage) > 0 { |
|
160 |
+ ref, err := imageapi.ParseDockerImageReference(o.FromImage) |
|
161 |
+ if err != nil { |
|
162 |
+ return fmt.Errorf("the value of --from-image does not appear to be a valid reference to an image: %v", err) |
|
163 |
+ } |
|
164 |
+ if len(ref.Registry) > 0 || len(ref.ID) > 0 { |
|
165 |
+ return fmt.Errorf("the value of --from-image must point to an image stream tag on this server") |
|
166 |
+ } |
|
167 |
+ if len(ref.Tag) == 0 { |
|
168 |
+ return fmt.Errorf("the value of --from-image must include the tag you wish to pull from") |
|
169 |
+ } |
|
170 |
+ o.FromImage = ref.NameString() |
|
171 |
+ o.FromImageNamespace = defaultNamespace(ref.Namespace, cmdNamespace) |
|
172 |
+ } |
|
173 |
+ |
|
174 |
+ count := o.count() |
|
175 |
+ o.Reset = count == 0 && (o.Auto || o.Manual) |
|
176 |
+ switch { |
|
177 |
+ case count == 0 && !o.Remove && !o.RemoveAll && !o.Auto && !o.Manual: |
|
178 |
+ o.PrintTable = true |
|
179 |
+ case !o.RemoveAll && !o.Auto && !o.Manual: |
|
180 |
+ o.Auto = true |
|
181 |
+ } |
|
182 |
+ |
|
183 |
+ mapper, typer := f.Object() |
|
184 |
+ o.Builder = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), kapi.Codecs.UniversalDecoder()). |
|
185 |
+ ContinueOnError(). |
|
186 |
+ NamespaceParam(cmdNamespace).DefaultNamespace(). |
|
187 |
+ FilenameParam(explicit, o.Filenames...). |
|
188 |
+ SelectorParam(o.Selector). |
|
189 |
+ ResourceTypeOrNameArgs(o.All, args...). |
|
190 |
+ Flatten() |
|
191 |
+ |
|
192 |
+ output := kcmdutil.GetFlagString(cmd, "output") |
|
193 |
+ if len(output) != 0 { |
|
194 |
+ o.PrintObject = func(obj runtime.Object) error { return f.PrintObject(cmd, obj, o.Out) } |
|
195 |
+ } |
|
196 |
+ |
|
197 |
+ o.Encoder = f.JSONEncoder() |
|
198 |
+ o.ShortOutput = kcmdutil.GetFlagString(cmd, "output") == "name" |
|
199 |
+ o.Mapper = mapper |
|
200 |
+ |
|
201 |
+ return nil |
|
202 |
+} |
|
203 |
+ |
|
204 |
+func (o *TriggersOptions) count() int { |
|
205 |
+ count := 0 |
|
206 |
+ if o.FromConfig { |
|
207 |
+ count++ |
|
208 |
+ } |
|
209 |
+ if o.FromGitHub != nil { |
|
210 |
+ count++ |
|
211 |
+ } |
|
212 |
+ if o.FromWebHook != nil { |
|
213 |
+ count++ |
|
214 |
+ } |
|
215 |
+ if len(o.FromImage) > 0 { |
|
216 |
+ count++ |
|
217 |
+ } |
|
218 |
+ return count |
|
219 |
+} |
|
220 |
+ |
|
221 |
+func (o *TriggersOptions) Validate() error { |
|
222 |
+ count := o.count() |
|
223 |
+ switch { |
|
224 |
+ case o.Auto && o.Manual: |
|
225 |
+ return fmt.Errorf("you must specify at most one of --auto or --manual") |
|
226 |
+ case o.Remove && o.RemoveAll: |
|
227 |
+ return fmt.Errorf("you must specify either --remove or --remove-all") |
|
228 |
+ case o.RemoveAll && (count != 0 || o.Auto || o.Manual): |
|
229 |
+ return fmt.Errorf("--remove-all may not be used with any other flag") |
|
230 |
+ case o.Remove && count < 1: |
|
231 |
+ return fmt.Errorf("--remove requires a flag defining a trigger type to be specified") |
|
232 |
+ case count > 1: |
|
233 |
+ return fmt.Errorf("you may only set one trigger type at a time") |
|
234 |
+ case count == 0 && !o.Remove && !o.RemoveAll && !o.Auto && !o.Manual && !o.PrintTable: |
|
235 |
+ return fmt.Errorf("specify one of the --from-* flags to add a trigger, --remove to remove, or --auto|--manual to control existing triggers") |
|
236 |
+ } |
|
237 |
+ return nil |
|
238 |
+} |
|
239 |
+ |
|
240 |
+func (o *TriggersOptions) Run() error { |
|
241 |
+ infos := o.Infos |
|
242 |
+ singular := len(o.Infos) <= 1 |
|
243 |
+ if o.Builder != nil { |
|
244 |
+ loaded, err := o.Builder.Do().IntoSingular(&singular).Infos() |
|
245 |
+ if err != nil { |
|
246 |
+ return err |
|
247 |
+ } |
|
248 |
+ infos = loaded |
|
249 |
+ } |
|
250 |
+ |
|
251 |
+ if o.PrintTable && o.PrintObject == nil { |
|
252 |
+ return o.printTriggers(infos) |
|
253 |
+ } |
|
254 |
+ |
|
255 |
+ updateTriggerFn := func(triggers *TriggerDefinition) error { |
|
256 |
+ o.updateTriggers(triggers) |
|
257 |
+ return nil |
|
258 |
+ } |
|
259 |
+ patches := CalculatePatches(infos, o.Encoder, func(info *resource.Info) (bool, error) { |
|
260 |
+ return UpdateTriggersForObject(info.Object, updateTriggerFn) |
|
261 |
+ }) |
|
262 |
+ if singular && len(patches) == 0 { |
|
263 |
+ return fmt.Errorf("%s/%s is not a deployment config or build config", infos[0].Mapping.Resource, infos[0].Name) |
|
264 |
+ } |
|
265 |
+ if len(patches) == 0 { |
|
266 |
+ return nil |
|
267 |
+ } |
|
268 |
+ |
|
269 |
+ if o.PrintObject != nil { |
|
270 |
+ var infos []*resource.Info |
|
271 |
+ for _, patch := range patches { |
|
272 |
+ info := patch.Info |
|
273 |
+ if patch.Err != nil { |
|
274 |
+ fmt.Fprintf(o.Err, "error: %s/%s %v\n", info.Mapping.Resource, info.Name, patch.Err) |
|
275 |
+ continue |
|
276 |
+ } |
|
277 |
+ infos = append(infos, info) |
|
278 |
+ } |
|
279 |
+ if len(infos) == 0 { |
|
280 |
+ return cmdutil.ErrExit |
|
281 |
+ } |
|
282 |
+ object, err := resource.AsVersionedObject(infos, !singular, "", nil) |
|
283 |
+ if err != nil { |
|
284 |
+ return err |
|
285 |
+ } |
|
286 |
+ return o.PrintObject(object) |
|
287 |
+ } |
|
288 |
+ |
|
289 |
+ failed := false |
|
290 |
+ for _, patch := range patches { |
|
291 |
+ info := patch.Info |
|
292 |
+ if patch.Err != nil { |
|
293 |
+ failed = true |
|
294 |
+ fmt.Fprintf(o.Err, "error: %s/%s %v\n", info.Mapping.Resource, info.Name, patch.Err) |
|
295 |
+ continue |
|
296 |
+ } |
|
297 |
+ |
|
298 |
+ if string(patch.Patch) == "{}" || len(patch.Patch) == 0 { |
|
299 |
+ fmt.Fprintf(o.Err, "info: %s %q was not changed\n", info.Mapping.Resource, info.Name) |
|
300 |
+ continue |
|
301 |
+ } |
|
302 |
+ |
|
303 |
+ glog.V(4).Infof("Calculated patch %s", patch.Patch) |
|
304 |
+ |
|
305 |
+ obj, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, kapi.StrategicMergePatchType, patch.Patch) |
|
306 |
+ if err != nil { |
|
307 |
+ handlePodUpdateError(o.Err, err, "triggered") |
|
308 |
+ failed = true |
|
309 |
+ continue |
|
310 |
+ } |
|
311 |
+ |
|
312 |
+ info.Refresh(obj, true) |
|
313 |
+ kcmdutil.PrintSuccess(o.Mapper, o.ShortOutput, o.Out, info.Mapping.Resource, info.Name, "updated") |
|
314 |
+ } |
|
315 |
+ if failed { |
|
316 |
+ return cmdutil.ErrExit |
|
317 |
+ } |
|
318 |
+ return nil |
|
319 |
+} |
|
320 |
+ |
|
321 |
+// printTriggers displays a tabular output of the triggers for each object. |
|
322 |
+func (o *TriggersOptions) printTriggers(infos []*resource.Info) error { |
|
323 |
+ w := tabwriter.NewWriter(o.Out, 0, 2, 2, ' ', 0) |
|
324 |
+ defer w.Flush() |
|
325 |
+ fmt.Fprintf(w, "NAME\tTYPE\tVALUE\tAUTO\n") |
|
326 |
+ for _, info := range infos { |
|
327 |
+ _, err := UpdateTriggersForObject(info.Object, func(triggers *TriggerDefinition) error { |
|
328 |
+ fmt.Fprintf(w, "%s/%s\t%s\t%s\t%t\n", info.Mapping.Resource, info.Name, "config", "", triggers.ConfigChange) |
|
329 |
+ for _, image := range triggers.ImageChange { |
|
330 |
+ var details string |
|
331 |
+ switch { |
|
332 |
+ case len(image.Names) > 0: |
|
333 |
+ if len(image.Namespace) > 0 { |
|
334 |
+ details = fmt.Sprintf("%s/%s (%s)", image.Namespace, image.From, strings.Join(image.Names, ", ")) |
|
335 |
+ } else { |
|
336 |
+ details = fmt.Sprintf("%s (%s)", image.From, strings.Join(image.Names, ", ")) |
|
337 |
+ } |
|
338 |
+ case len(image.Namespace) > 0: |
|
339 |
+ details = fmt.Sprintf("%s/%s", image.Namespace, image.From) |
|
340 |
+ default: |
|
341 |
+ details = image.From |
|
342 |
+ } |
|
343 |
+ fmt.Fprintf(w, "%s/%s\t%s\t%s\t%t\n", info.Mapping.Resource, info.Name, "image", details, image.Auto) |
|
344 |
+ } |
|
345 |
+ for _, s := range triggers.WebHooks { |
|
346 |
+ fmt.Fprintf(w, "%s/%s\t%s\t%s\t%s\n", info.Mapping.Resource, info.Name, "webhook", s, "") |
|
347 |
+ } |
|
348 |
+ for _, s := range triggers.GitHubWebHooks { |
|
349 |
+ fmt.Fprintf(w, "%s/%s\t%s\t%s\t%s\n", info.Mapping.Resource, info.Name, "github", s, "") |
|
350 |
+ } |
|
351 |
+ return nil |
|
352 |
+ }) |
|
353 |
+ if err != nil { |
|
354 |
+ fmt.Fprintf(w, "%s/%s\t%s\t%s\t%t\n", info.Mapping.Resource, info.Name, "<error>", "", false) |
|
355 |
+ } |
|
356 |
+ } |
|
357 |
+ return nil |
|
358 |
+} |
|
359 |
+ |
|
360 |
+// updateTriggers updates only those fields with flags set by the user |
|
361 |
+func (o *TriggersOptions) updateTriggers(triggers *TriggerDefinition) { |
|
362 |
+ // clear everything |
|
363 |
+ if o.RemoveAll { |
|
364 |
+ *triggers = TriggerDefinition{} |
|
365 |
+ return |
|
366 |
+ } |
|
367 |
+ |
|
368 |
+ // clear a specific field |
|
369 |
+ if o.Remove { |
|
370 |
+ if o.FromConfig { |
|
371 |
+ triggers.ConfigChange = false |
|
372 |
+ } |
|
373 |
+ if len(o.FromImage) > 0 { |
|
374 |
+ var newTriggers []ImageChangeTrigger |
|
375 |
+ for _, trigger := range triggers.ImageChange { |
|
376 |
+ if trigger.From != o.FromImage { |
|
377 |
+ newTriggers = append(newTriggers, trigger) |
|
378 |
+ } |
|
379 |
+ } |
|
380 |
+ triggers.ImageChange = newTriggers |
|
381 |
+ } |
|
382 |
+ if o.FromWebHook != nil && *o.FromWebHook { |
|
383 |
+ triggers.WebHooks = nil |
|
384 |
+ } |
|
385 |
+ if o.FromGitHub != nil && *o.FromGitHub { |
|
386 |
+ triggers.GitHubWebHooks = nil |
|
387 |
+ } |
|
388 |
+ return |
|
389 |
+ } |
|
390 |
+ |
|
391 |
+ // change the automated status |
|
392 |
+ if o.Reset { |
|
393 |
+ triggers.ConfigChange = o.Auto |
|
394 |
+ for i := range triggers.ImageChange { |
|
395 |
+ triggers.ImageChange[i].Auto = o.Auto |
|
396 |
+ } |
|
397 |
+ return |
|
398 |
+ } |
|
399 |
+ |
|
400 |
+ // change individual elements |
|
401 |
+ if o.FromConfig { |
|
402 |
+ triggers.ConfigChange = true |
|
403 |
+ } |
|
404 |
+ if len(o.FromImage) > 0 { |
|
405 |
+ names := strings.Split(o.ContainerNames, ",") |
|
406 |
+ if len(o.ContainerNames) == 0 { |
|
407 |
+ names = nil |
|
408 |
+ } |
|
409 |
+ found := false |
|
410 |
+ for i, trigger := range triggers.ImageChange { |
|
411 |
+ if trigger.From == o.FromImage && trigger.Namespace == o.FromImageNamespace { |
|
412 |
+ found = true |
|
413 |
+ triggers.ImageChange[i].Auto = !o.Manual |
|
414 |
+ triggers.ImageChange[i].Names = names |
|
415 |
+ break |
|
416 |
+ } |
|
417 |
+ } |
|
418 |
+ if !found { |
|
419 |
+ triggers.ImageChange = append(triggers.ImageChange, ImageChangeTrigger{ |
|
420 |
+ From: o.FromImage, |
|
421 |
+ Namespace: o.FromImageNamespace, |
|
422 |
+ Auto: !o.Manual, |
|
423 |
+ Names: names, |
|
424 |
+ }) |
|
425 |
+ } |
|
426 |
+ } |
|
427 |
+ if o.FromWebHook != nil && *o.FromWebHook { |
|
428 |
+ triggers.WebHooks = []string{app.GenerateSecret(20)} |
|
429 |
+ } |
|
430 |
+ if o.FromGitHub != nil && *o.FromGitHub { |
|
431 |
+ triggers.GitHubWebHooks = []string{app.GenerateSecret(20)} |
|
432 |
+ } |
|
433 |
+} |
|
434 |
+ |
|
435 |
+// ImageChangeTrigger represents the capabilities present in deployment config and build |
|
436 |
+// config objects in a consistent way. |
|
437 |
+type ImageChangeTrigger struct { |
|
438 |
+ // If this trigger is automatically applied |
|
439 |
+ Auto bool |
|
440 |
+ // An ImageStreamTag name to target |
|
441 |
+ From string |
|
442 |
+ // The target namespace, normalized if set |
|
443 |
+ Namespace string |
|
444 |
+ // A list of names this trigger targets |
|
445 |
+ Names []string |
|
446 |
+} |
|
447 |
+ |
|
448 |
+// TriggerDefinition is the abstract representation of triggers for builds and deploymnet configs. |
|
449 |
+type TriggerDefinition struct { |
|
450 |
+ ConfigChange bool |
|
451 |
+ ImageChange []ImageChangeTrigger |
|
452 |
+ WebHooks []string |
|
453 |
+ GitHubWebHooks []string |
|
454 |
+} |
|
455 |
+ |
|
456 |
+// defaultNamespace returns an empty string if the provided namespace matches the default namespace, or |
|
457 |
+// returns the namespace. |
|
458 |
+func defaultNamespace(namespace, defaultNamespace string) string { |
|
459 |
+ if namespace == defaultNamespace { |
|
460 |
+ return "" |
|
461 |
+ } |
|
462 |
+ return namespace |
|
463 |
+} |
|
464 |
+ |
|
465 |
+// NewDeploymentConfigTriggers creates a trigger definition from a deployment config. |
|
466 |
+func NewDeploymentConfigTriggers(config *deployapi.DeploymentConfig) *TriggerDefinition { |
|
467 |
+ t := &TriggerDefinition{} |
|
468 |
+ for _, trigger := range config.Spec.Triggers { |
|
469 |
+ switch trigger.Type { |
|
470 |
+ case deployapi.DeploymentTriggerOnConfigChange: |
|
471 |
+ t.ConfigChange = true |
|
472 |
+ case deployapi.DeploymentTriggerOnImageChange: |
|
473 |
+ t.ImageChange = append(t.ImageChange, ImageChangeTrigger{ |
|
474 |
+ Auto: trigger.ImageChangeParams.Automatic, |
|
475 |
+ Names: trigger.ImageChangeParams.ContainerNames, |
|
476 |
+ From: trigger.ImageChangeParams.From.Name, |
|
477 |
+ Namespace: defaultNamespace(trigger.ImageChangeParams.From.Namespace, config.Namespace), |
|
478 |
+ }) |
|
479 |
+ } |
|
480 |
+ } |
|
481 |
+ return t |
|
482 |
+} |
|
483 |
+ |
|
484 |
+// NewBuildConfigTriggers creates a trigger definition from a build config. |
|
485 |
+func NewBuildConfigTriggers(config *buildapi.BuildConfig) *TriggerDefinition { |
|
486 |
+ t := &TriggerDefinition{} |
|
487 |
+ setStrategy := false |
|
488 |
+ for _, trigger := range config.Spec.Triggers { |
|
489 |
+ switch trigger.Type { |
|
490 |
+ case buildapi.ConfigChangeBuildTriggerType: |
|
491 |
+ t.ConfigChange = true |
|
492 |
+ case buildapi.GenericWebHookBuildTriggerType: |
|
493 |
+ t.WebHooks = append(t.WebHooks, trigger.GenericWebHook.Secret) |
|
494 |
+ case buildapi.GitHubWebHookBuildTriggerType: |
|
495 |
+ t.GitHubWebHooks = append(t.GitHubWebHooks, trigger.GitHubWebHook.Secret) |
|
496 |
+ case buildapi.ImageChangeBuildTriggerType: |
|
497 |
+ if trigger.ImageChange.From == nil { |
|
498 |
+ if strategyTrigger := strategyTrigger(config); strategyTrigger != nil { |
|
499 |
+ setStrategy = true |
|
500 |
+ strategyTrigger.Auto = true |
|
501 |
+ t.ImageChange = append(t.ImageChange, *strategyTrigger) |
|
502 |
+ } |
|
503 |
+ continue |
|
504 |
+ } |
|
505 |
+ // normalize the trigger |
|
506 |
+ trigger.ImageChange.From.Namespace = defaultNamespace(trigger.ImageChange.From.Namespace, config.Namespace) |
|
507 |
+ t.ImageChange = append(t.ImageChange, ImageChangeTrigger{ |
|
508 |
+ Auto: true, |
|
509 |
+ From: trigger.ImageChange.From.Name, |
|
510 |
+ Namespace: trigger.ImageChange.From.Namespace, |
|
511 |
+ }) |
|
512 |
+ } |
|
513 |
+ } |
|
514 |
+ if !setStrategy { |
|
515 |
+ if strategyTrigger := strategyTrigger(config); strategyTrigger != nil { |
|
516 |
+ t.ImageChange = append(t.ImageChange, *strategyTrigger) |
|
517 |
+ } |
|
518 |
+ } |
|
519 |
+ return t |
|
520 |
+} |
|
521 |
+ |
|
522 |
+// Apply writes a trigger definition back to a build or deployment config. |
|
523 |
+func (t *TriggerDefinition) Apply(obj runtime.Object) error { |
|
524 |
+ switch c := obj.(type) { |
|
525 |
+ case *deployapi.DeploymentConfig: |
|
526 |
+ if len(t.GitHubWebHooks) > 0 { |
|
527 |
+ return fmt.Errorf("deployment configs do not support GitHub web hooks") |
|
528 |
+ } |
|
529 |
+ if len(t.WebHooks) > 0 { |
|
530 |
+ return fmt.Errorf("deployment configs do not support web hooks") |
|
531 |
+ } |
|
532 |
+ |
|
533 |
+ existingTriggers := filterDeploymentTriggers(c.Spec.Triggers, deployapi.DeploymentTriggerOnConfigChange) |
|
534 |
+ var triggers []deployapi.DeploymentTriggerPolicy |
|
535 |
+ if t.ConfigChange { |
|
536 |
+ triggers = append(triggers, deployapi.DeploymentTriggerPolicy{Type: deployapi.DeploymentTriggerOnConfigChange}) |
|
537 |
+ } |
|
538 |
+ allNames := sets.NewString() |
|
539 |
+ for _, container := range c.Spec.Template.Spec.Containers { |
|
540 |
+ allNames.Insert(container.Name) |
|
541 |
+ } |
|
542 |
+ for _, trigger := range t.ImageChange { |
|
543 |
+ if len(trigger.Names) == 0 { |
|
544 |
+ return fmt.Errorf("you must specify --containers when setting --from-image") |
|
545 |
+ } |
|
546 |
+ if !allNames.HasAll(trigger.Names...) { |
|
547 |
+ return fmt.Errorf( |
|
548 |
+ "not all container names exist: %s (accepts: %s)", |
|
549 |
+ strings.Join(sets.NewString(trigger.Names...).Difference(allNames).List(), ", "), |
|
550 |
+ strings.Join(allNames.List(), ", "), |
|
551 |
+ ) |
|
552 |
+ } |
|
553 |
+ triggers = append(triggers, deployapi.DeploymentTriggerPolicy{ |
|
554 |
+ Type: deployapi.DeploymentTriggerOnImageChange, |
|
555 |
+ ImageChangeParams: &deployapi.DeploymentTriggerImageChangeParams{ |
|
556 |
+ Automatic: trigger.Auto, |
|
557 |
+ From: kapi.ObjectReference{ |
|
558 |
+ Kind: "ImageStreamTag", |
|
559 |
+ Name: trigger.From, |
|
560 |
+ }, |
|
561 |
+ ContainerNames: trigger.Names, |
|
562 |
+ }, |
|
563 |
+ }) |
|
564 |
+ } |
|
565 |
+ c.Spec.Triggers = mergeDeployTriggers(existingTriggers, triggers) |
|
566 |
+ return nil |
|
567 |
+ |
|
568 |
+ case *buildapi.BuildConfig: |
|
569 |
+ var triggers []buildapi.BuildTriggerPolicy |
|
570 |
+ if t.ConfigChange { |
|
571 |
+ triggers = append(triggers, buildapi.BuildTriggerPolicy{Type: buildapi.ConfigChangeBuildTriggerType}) |
|
572 |
+ } |
|
573 |
+ for _, trigger := range t.WebHooks { |
|
574 |
+ triggers = append(triggers, buildapi.BuildTriggerPolicy{ |
|
575 |
+ Type: buildapi.GenericWebHookBuildTriggerType, |
|
576 |
+ GenericWebHook: &buildapi.WebHookTrigger{ |
|
577 |
+ Secret: trigger, |
|
578 |
+ }, |
|
579 |
+ }) |
|
580 |
+ } |
|
581 |
+ for _, trigger := range t.GitHubWebHooks { |
|
582 |
+ triggers = append(triggers, buildapi.BuildTriggerPolicy{ |
|
583 |
+ Type: buildapi.GitHubWebHookBuildTriggerType, |
|
584 |
+ GitHubWebHook: &buildapi.WebHookTrigger{ |
|
585 |
+ Secret: trigger, |
|
586 |
+ }, |
|
587 |
+ }) |
|
588 |
+ } |
|
589 |
+ |
|
590 |
+ // add new triggers, filter out any old triggers that match (if moving from automatic to manual), |
|
591 |
+ // and then merge the old triggers and the new triggers to preserve fields like lastTriggeredImageID |
|
592 |
+ existingTriggers := c.Spec.Triggers |
|
593 |
+ strategyTrigger := strategyTrigger(c) |
|
594 |
+ for _, trigger := range t.ImageChange { |
|
595 |
+ change := &buildapi.ImageChangeTrigger{ |
|
596 |
+ From: &kapi.ObjectReference{ |
|
597 |
+ Kind: "ImageStreamTag", |
|
598 |
+ Name: trigger.From, |
|
599 |
+ Namespace: trigger.Namespace, |
|
600 |
+ }, |
|
601 |
+ } |
|
602 |
+ |
|
603 |
+ // use the canonical ImageChangeTrigger with nil From |
|
604 |
+ strategyTrigger.Auto = trigger.Auto |
|
605 |
+ if reflect.DeepEqual(strategyTrigger, &trigger) { |
|
606 |
+ change.From = nil |
|
607 |
+ } |
|
608 |
+ |
|
609 |
+ // if this trigger is not automatic, then we need to remove it from the list of triggers |
|
610 |
+ if !trigger.Auto { |
|
611 |
+ existingTriggers = filterBuildImageTriggers(existingTriggers, trigger, strategyTrigger) |
|
612 |
+ continue |
|
613 |
+ } |
|
614 |
+ |
|
615 |
+ triggers = append(triggers, buildapi.BuildTriggerPolicy{ |
|
616 |
+ Type: buildapi.ImageChangeBuildTriggerType, |
|
617 |
+ ImageChange: change, |
|
618 |
+ }) |
|
619 |
+ } |
|
620 |
+ c.Spec.Triggers = mergeBuildTriggers(existingTriggers, triggers) |
|
621 |
+ return nil |
|
622 |
+ |
|
623 |
+ default: |
|
624 |
+ return fmt.Errorf("the object is not a deployment config or build config") |
|
625 |
+ } |
|
626 |
+} |
|
627 |
+ |
|
628 |
+// triggerMatchesBuildImageChange identifies whether the image change is equivalent to the trigger |
|
629 |
+func triggerMatchesBuildImageChange(trigger ImageChangeTrigger, strategyTrigger *ImageChangeTrigger, imageChange *buildapi.ImageChangeTrigger) bool { |
|
630 |
+ if imageChange == nil { |
|
631 |
+ return false |
|
632 |
+ } |
|
633 |
+ if imageChange.From == nil { |
|
634 |
+ return strategyTrigger != nil && strategyTrigger.From == trigger.From && strategyTrigger.Namespace == trigger.Namespace |
|
635 |
+ } |
|
636 |
+ namespace := imageChange.From.Namespace |
|
637 |
+ if strategyTrigger != nil { |
|
638 |
+ namespace = defaultNamespace(namespace, strategyTrigger.Namespace) |
|
639 |
+ } |
|
640 |
+ return imageChange.From.Name == trigger.From && namespace == trigger.Namespace |
|
641 |
+} |
|
642 |
+ |
|
643 |
+// filterBuildImageTriggers return only triggers that do not match the provided ImageChangeTrigger. strategyTrigger may be provided |
|
644 |
+// if set to remove a BuildTriggerPolicy without a From (which points to the strategy) |
|
645 |
+func filterBuildImageTriggers(src []buildapi.BuildTriggerPolicy, trigger ImageChangeTrigger, strategyTrigger *ImageChangeTrigger) []buildapi.BuildTriggerPolicy { |
|
646 |
+ var dst []buildapi.BuildTriggerPolicy |
|
647 |
+ for i := range src { |
|
648 |
+ if triggerMatchesBuildImageChange(trigger, strategyTrigger, src[i].ImageChange) { |
|
649 |
+ continue |
|
650 |
+ } |
|
651 |
+ dst = append(dst, src[i]) |
|
652 |
+ } |
|
653 |
+ return dst |
|
654 |
+} |
|
655 |
+ |
|
656 |
+// filterDeploymentTriggers returns only triggers that do not have one of the provided types. |
|
657 |
+func filterDeploymentTriggers(src []deployapi.DeploymentTriggerPolicy, types ...deployapi.DeploymentTriggerType) []deployapi.DeploymentTriggerPolicy { |
|
658 |
+ var dst []deployapi.DeploymentTriggerPolicy |
|
659 |
+Outer: |
|
660 |
+ for i := range src { |
|
661 |
+ for _, t := range types { |
|
662 |
+ if t == src[i].Type { |
|
663 |
+ continue Outer |
|
664 |
+ } |
|
665 |
+ } |
|
666 |
+ dst = append(dst, src[i]) |
|
667 |
+ } |
|
668 |
+ return dst |
|
669 |
+} |
|
670 |
+ |
|
671 |
+// strategyTrigger returns a synthetic ImageChangeTrigger that represents the image stream tag the build strategy |
|
672 |
+// points to, or nil if no such strategy trigger is possible (if the build doesn't point to an ImageStreamTag). |
|
673 |
+func strategyTrigger(config *buildapi.BuildConfig) *ImageChangeTrigger { |
|
674 |
+ if from := buildutil.GetImageStreamForStrategy(config.Spec.Strategy); from != nil { |
|
675 |
+ if from.Kind == "ImageStreamTag" { |
|
676 |
+ // normalize the strategy object reference |
|
677 |
+ from.Namespace = defaultNamespace(from.Namespace, config.Namespace) |
|
678 |
+ return &ImageChangeTrigger{From: from.Name, Namespace: from.Namespace} |
|
679 |
+ } |
|
680 |
+ } |
|
681 |
+ return nil |
|
682 |
+} |
|
683 |
+ |
|
684 |
+// mergeDeployTriggers returns an array of DeploymentTriggerPolicies that have no duplicates. |
|
685 |
+func mergeDeployTriggers(dst, src []deployapi.DeploymentTriggerPolicy) []deployapi.DeploymentTriggerPolicy { |
|
686 |
+ // never return an empty map, because the triggers on a deployment config default when the map is empty |
|
687 |
+ result := []deployapi.DeploymentTriggerPolicy{} |
|
688 |
+ for _, current := range dst { |
|
689 |
+ if findDeployTrigger(src, current) != -1 { |
|
690 |
+ result = append(result, current) |
|
691 |
+ } |
|
692 |
+ } |
|
693 |
+ for _, current := range src { |
|
694 |
+ if findDeployTrigger(result, current) == -1 { |
|
695 |
+ result = append(result, current) |
|
696 |
+ } |
|
697 |
+ } |
|
698 |
+ return result |
|
699 |
+} |
|
700 |
+ |
|
701 |
+// findDeployTrigger finds the position of a deployment trigger in the provided array, or -1 if no such |
|
702 |
+// matching trigger is found. |
|
703 |
+func findDeployTrigger(dst []deployapi.DeploymentTriggerPolicy, trigger deployapi.DeploymentTriggerPolicy) int { |
|
704 |
+ for i := range dst { |
|
705 |
+ if reflect.DeepEqual(dst[i], trigger) { |
|
706 |
+ return i |
|
707 |
+ } |
|
708 |
+ } |
|
709 |
+ return -1 |
|
710 |
+} |
|
711 |
+ |
|
712 |
+// mergeBuildTriggers returns an array of BuildTriggerPolicies that have no duplicates, in the same order |
|
713 |
+// as they exist in their original arrays (a zip-merge). |
|
714 |
+func mergeBuildTriggers(dst, src []buildapi.BuildTriggerPolicy) []buildapi.BuildTriggerPolicy { |
|
715 |
+ var result []buildapi.BuildTriggerPolicy |
|
716 |
+ for _, current := range dst { |
|
717 |
+ if findBuildTrigger(src, current) != -1 { |
|
718 |
+ result = append(result, current) |
|
719 |
+ } |
|
720 |
+ } |
|
721 |
+ for _, current := range src { |
|
722 |
+ if findBuildTrigger(result, current) == -1 { |
|
723 |
+ result = append(result, current) |
|
724 |
+ } |
|
725 |
+ } |
|
726 |
+ return result |
|
727 |
+} |
|
728 |
+ |
|
729 |
+// findBuildTrigger finds the equivalent build trigger position in the provided array, or -1 if |
|
730 |
+// no such build trigger exists. Equality only cares about the value of the From field. |
|
731 |
+func findBuildTrigger(dst []buildapi.BuildTriggerPolicy, trigger buildapi.BuildTriggerPolicy) int { |
|
732 |
+ // make a copy for semantic equality |
|
733 |
+ if trigger.ImageChange != nil { |
|
734 |
+ trigger.ImageChange = &buildapi.ImageChangeTrigger{From: trigger.ImageChange.From} |
|
735 |
+ } |
|
736 |
+ for i, copied := range dst { |
|
737 |
+ // make a copy for semantic equality |
|
738 |
+ if copied.ImageChange != nil { |
|
739 |
+ copied.ImageChange = &buildapi.ImageChangeTrigger{From: copied.ImageChange.From} |
|
740 |
+ } |
|
741 |
+ if reflect.DeepEqual(copied, trigger) { |
|
742 |
+ return i |
|
743 |
+ } |
|
744 |
+ } |
|
745 |
+ return -1 |
|
746 |
+} |
|
747 |
+ |
|
748 |
+// UpdateTriggersForObject extracts a trigger definition from the provided object, passes it to fn, and |
|
749 |
+// then applies the trigger definition back on the object. It returns true if the object was mutated |
|
750 |
+// and an optional error if the any part of the flow returns error. |
|
751 |
+func UpdateTriggersForObject(obj runtime.Object, fn func(*TriggerDefinition) error) (bool, error) { |
|
752 |
+ // TODO: replace with a swagger schema based approach (identify pod template via schema introspection) |
|
753 |
+ switch t := obj.(type) { |
|
754 |
+ case *deployapi.DeploymentConfig: |
|
755 |
+ triggers := NewDeploymentConfigTriggers(t) |
|
756 |
+ if err := fn(triggers); err != nil { |
|
757 |
+ return true, err |
|
758 |
+ } |
|
759 |
+ return true, triggers.Apply(t) |
|
760 |
+ case *buildapi.BuildConfig: |
|
761 |
+ triggers := NewBuildConfigTriggers(t) |
|
762 |
+ if err := fn(triggers); err != nil { |
|
763 |
+ return true, err |
|
764 |
+ } |
|
765 |
+ return true, triggers.Apply(t) |
|
766 |
+ default: |
|
767 |
+ return false, fmt.Errorf("the object is not a deployment config or build config") |
|
768 |
+ } |
|
769 |
+} |
... | ... |
@@ -132,13 +132,13 @@ func (r *SourceRef) BuildSource() (*buildapi.BuildSource, []buildapi.BuildTrigge |
132 | 132 |
{ |
133 | 133 |
Type: buildapi.GitHubWebHookBuildTriggerType, |
134 | 134 |
GitHubWebHook: &buildapi.WebHookTrigger{ |
135 |
- Secret: generateSecret(20), |
|
135 |
+ Secret: GenerateSecret(20), |
|
136 | 136 |
}, |
137 | 137 |
}, |
138 | 138 |
{ |
139 | 139 |
Type: buildapi.GenericWebHookBuildTriggerType, |
140 | 140 |
GenericWebHook: &buildapi.WebHookTrigger{ |
141 |
- Secret: generateSecret(20), |
|
141 |
+ Secret: GenerateSecret(20), |
|
142 | 142 |
}, |
143 | 143 |
}, |
144 | 144 |
} |
... | ... |
@@ -358,8 +358,8 @@ func (r *DeploymentConfigRef) DeploymentConfig() (*deployapi.DeploymentConfig, e |
358 | 358 |
}, nil |
359 | 359 |
} |
360 | 360 |
|
361 |
-// generateSecret generates a random secret string |
|
362 |
-func generateSecret(n int) string { |
|
361 |
+// GenerateSecret generates a random secret string |
|
362 |
+func GenerateSecret(n int) string { |
|
363 | 363 |
n = n * 3 / 4 |
364 | 364 |
b := make([]byte, n) |
365 | 365 |
read, _ := rand.Read(b) |
366 | 366 |
new file mode 100755 |
... | ... |
@@ -0,0 +1,88 @@ |
0 |
+#!/bin/bash |
|
1 |
+ |
|
2 |
+set -o errexit |
|
3 |
+set -o nounset |
|
4 |
+set -o pipefail |
|
5 |
+ |
|
6 |
+OS_ROOT=$(dirname "${BASH_SOURCE}")/../.. |
|
7 |
+source "${OS_ROOT}/hack/util.sh" |
|
8 |
+source "${OS_ROOT}/hack/cmd_util.sh" |
|
9 |
+os::log::install_errexit |
|
10 |
+ |
|
11 |
+# Cleanup cluster resources created by this test |
|
12 |
+( |
|
13 |
+ set +e |
|
14 |
+ oc delete all --all |
|
15 |
+ exit 0 |
|
16 |
+) &>/dev/null |
|
17 |
+ |
|
18 |
+ |
|
19 |
+url=":${API_PORT:-8443}" |
|
20 |
+project="$(oc project -q)" |
|
21 |
+ |
|
22 |
+# This test validates builds and build related commands |
|
23 |
+ |
|
24 |
+os::cmd::expect_success 'oc new-app centos/ruby-22-centos7~https://github.com/openshift/ruby-hello-world.git' |
|
25 |
+os::cmd::expect_success 'oc get bc/ruby-hello-world' |
|
26 |
+os::cmd::expect_success 'oc get dc/ruby-hello-world' |
|
27 |
+ |
|
28 |
+## Build configs |
|
29 |
+ |
|
30 |
+# error conditions |
|
31 |
+os::cmd::expect_failure_and_text 'oc set triggers bc/ruby-hello-world --remove --remove-all' 'specify either --remove or --remove-all' |
|
32 |
+os::cmd::expect_failure_and_text 'oc set triggers bc/ruby-hello-world --auto --manual' 'at most one of --auto or --manual' |
|
33 |
|
|
34 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world' 'config.*true' |
|
35 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world' 'image.*ruby-22-centos7:latest.*true' |
|
36 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world' 'webhook' |
|
37 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world' 'github' |
|
38 |
+# remove all |
|
39 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world --remove-all' 'updated' |
|
40 |
+os::cmd::expect_success_and_not_text 'oc set triggers bc/ruby-hello-world' 'webhook|github' |
|
41 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world' 'config.*false' |
|
42 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world' 'image.*ruby-22-centos7:latest.*false' |
|
43 |
+# set github hook |
|
44 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world --from-github' 'updated' |
|
45 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world' 'github' |
|
46 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world --remove --from-github' 'updated' |
|
47 |
+os::cmd::expect_success_and_not_text 'oc set triggers bc/ruby-hello-world' 'github' |
|
48 |
+# set webhook |
|
49 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world --from-webhook' 'updated' |
|
50 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world' 'webhook' |
|
51 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world --remove --from-webhook' 'updated' |
|
52 |
+os::cmd::expect_success_and_not_text 'oc set triggers bc/ruby-hello-world' 'webhook' |
|
53 |
+# set from-image |
|
54 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world --from-image=ruby-22-centos7:other' 'updated' |
|
55 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world' 'image.*ruby-22-centos7:other.*true' |
|
56 |
+# manual and remove both clear build configs |
|
57 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world --from-image=ruby-22-centos7:other --manual' 'updated' |
|
58 |
+os::cmd::expect_success_and_not_text 'oc set triggers bc/ruby-hello-world' 'image.*ruby-22-centos7:other.*false' |
|
59 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world --from-image=ruby-22-centos7:other' 'updated' |
|
60 |
+os::cmd::expect_success_and_text 'oc set triggers bc/ruby-hello-world --from-image=ruby-22-centos7:other --remove' 'updated' |
|
61 |
+os::cmd::expect_success_and_not_text 'oc set triggers bc/ruby-hello-world' 'image.*ruby-22-centos7:other' |
|
62 |
+# test --all |
|
63 |
+os::cmd::expect_success_and_text 'oc set triggers bc --all' 'buildconfigs/ruby-hello-world.*image.*ruby-22-centos7:latest.*false' |
|
64 |
+os::cmd::expect_success_and_text 'oc set triggers bc --all --auto' 'updated' |
|
65 |
+os::cmd::expect_success_and_text 'oc set triggers bc --all' 'buildconfigs/ruby-hello-world.*image.*ruby-22-centos7:latest.*true' |
|
66 |
+ |
|
67 |
+## Deployment configs |
|
68 |
+ |
|
69 |
+# error conditions |
|
70 |
+os::cmd::expect_failure_and_text 'oc set triggers dc/ruby-hello-world --from-github' 'deployment configs do not support GitHub web hooks' |
|
71 |
+os::cmd::expect_failure_and_text 'oc set triggers dc/ruby-hello-world --from-webhook' 'deployment configs do not support web hooks' |
|
72 |
+os::cmd::expect_failure_and_text 'oc set triggers dc/ruby-hello-world --from-image=test:latest' 'you must specify --containers when setting --from-image' |
|
73 |
+os::cmd::expect_failure_and_text 'oc set triggers dc/ruby-hello-world --from-image=test:latest --containers=other' 'not all container names exist: other \(accepts: ruby-hello-world\)' |
|
74 |
|
|
75 |
+os::cmd::expect_success_and_text 'oc set triggers dc/ruby-hello-world' 'config.*true' |
|
76 |
+os::cmd::expect_success_and_text 'oc set triggers dc/ruby-hello-world' 'image.*ruby-hello-world:latest \(ruby-hello-world\).*true' |
|
77 |
+os::cmd::expect_success_and_not_text 'oc set triggers dc/ruby-hello-world' 'webhook' |
|
78 |
+os::cmd::expect_success_and_not_text 'oc set triggers dc/ruby-hello-world' 'github' |
|
79 |
+# remove all |
|
80 |
+os::cmd::expect_success_and_text 'oc set triggers dc/ruby-hello-world --remove-all' 'updated' |
|
81 |
+os::cmd::expect_success_and_not_text 'oc set triggers dc/ruby-hello-world' 'webhook|github|image' |
|
82 |
+os::cmd::expect_success_and_text 'oc set triggers dc/ruby-hello-world' 'config.*false' |
|
83 |
+# auto |
|
84 |
+os::cmd::expect_success_and_text 'oc set triggers dc/ruby-hello-world --auto' 'updated' |
|
85 |
+os::cmd::expect_success_and_text 'oc set triggers dc/ruby-hello-world' 'config.*true' |
|
86 |
+os::cmd::expect_success_and_text 'oc set triggers dc/ruby-hello-world --from-image=ruby-hello-world:latest -c ruby-hello-world' 'updated' |
|
87 |
+os::cmd::expect_success_and_text 'oc set triggers dc/ruby-hello-world' 'image.*ruby-hello-world:latest \(ruby-hello-world\).*true' |