304a2b2b |
package set
import (
"fmt"
"io"
"os"
"reflect"
"strings"
"text/tabwriter"
"github.com/golang/glog"
"github.com/spf13/cobra"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta" |
5a556e67 |
"k8s.io/kubernetes/pkg/api/unversioned" |
304a2b2b |
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
buildapi "github.com/openshift/origin/pkg/build/api"
buildutil "github.com/openshift/origin/pkg/build/util" |
6267dded |
"github.com/openshift/origin/pkg/cmd/templates" |
304a2b2b |
cmdutil "github.com/openshift/origin/pkg/cmd/util"
"github.com/openshift/origin/pkg/cmd/util/clientcmd"
deployapi "github.com/openshift/origin/pkg/deploy/api"
"github.com/openshift/origin/pkg/generate/app"
imageapi "github.com/openshift/origin/pkg/image/api"
"k8s.io/kubernetes/pkg/util/sets"
)
|
6267dded |
var (
triggersLong = templates.LongDesc(`
Set or remove triggers for build configs and deployment configs |
304a2b2b |
|
6267dded |
All build configs and deployment configs may have a set of triggers that result in a new deployment
or build being created. This command enables you to alter those triggers - making them automatic or
manual, adding new entries, or changing existing entries. |
304a2b2b |
|
6267dded |
Deployments support triggering off of image changes and on config changes. Config changes are any
alterations to the pod template, while image changes will result in the container image value being
updated whenever an image stream tag is updated. |
304a2b2b |
|
6267dded |
Build configs support triggering off of image changes, config changes, and webhooks (both GitHub-specific
and generic). The config change trigger for a build config will only trigger the first build.`) |
304a2b2b |
|
6267dded |
triggersExample = templates.Examples(`
# Print the triggers on the registry
%[1]s triggers dc/registry |
304a2b2b |
|
6267dded |
# Set all triggers to manual
%[1]s triggers dc/registry --manual |
304a2b2b |
|
6267dded |
# Enable all automatic triggers
%[1]s triggers dc/registry --auto |
304a2b2b |
|
6267dded |
# Reset the GitHub webhook on a build to a new, generated secret
%[1]s triggers bc/webapp --from-github
%[1]s triggers bc/webapp --from-webhook |
304a2b2b |
|
6267dded |
# Remove all triggers
%[1]s triggers bc/webapp --remove-all |
304a2b2b |
|
6267dded |
# Stop triggering on config change
%[1]s triggers dc/registry --from-config --remove |
304a2b2b |
|
6267dded |
# Add an image trigger to a build config
%[1]s triggers bc/webapp --from-image=namespace1/image:latest`) |
304a2b2b |
)
type TriggersOptions struct {
Out io.Writer
Err io.Writer
Filenames []string
Selector string
All bool
Builder *resource.Builder
Infos []*resource.Info
Encoder runtime.Encoder
|
5a556e67 |
ShortOutput bool
Mapper meta.RESTMapper
OutputVersion unversioned.GroupVersion |
304a2b2b |
PrintTable bool |
0650f99d |
PrintObject func([]*resource.Info) error |
304a2b2b |
Remove bool
RemoveAll bool
Auto bool
Manual bool
Reset bool
|
150c7c58 |
ContainerNames string
FromConfig bool
FromGitHub *bool
FromWebHook *bool
FromWebHookAllowEnv *bool
FromImage string |
304a2b2b |
// FromImageNamespace is the namespace for the FromImage
FromImageNamespace string
}
// NewCmdTriggers implements the set triggers command
func NewCmdTriggers(fullName string, f *clientcmd.Factory, out, errOut io.Writer) *cobra.Command {
options := &TriggersOptions{
Out: out,
Err: errOut,
}
cmd := &cobra.Command{
Use: "triggers RESOURCE/NAME [--from-config|--from-image|--from-github|--from-webhook] [--auto|--manual]",
Short: "Update the triggers on a build or deployment config",
Long: triggersLong,
Example: fmt.Sprintf(triggersExample, fullName),
Run: func(cmd *cobra.Command, args []string) {
kcmdutil.CheckErr(options.Complete(f, cmd, args))
kcmdutil.CheckErr(options.Validate())
if err := options.Run(); err != nil { |
d7da290f |
// TODO: move me to kcmdutil |
304a2b2b |
if err == cmdutil.ErrExit {
os.Exit(1)
}
kcmdutil.CheckErr(err)
}
},
}
kcmdutil.AddPrinterFlags(cmd)
cmd.Flags().StringVarP(&options.Selector, "selector", "l", options.Selector, "Selector (label query) to filter on") |
9c93da29 |
cmd.Flags().BoolVar(&options.All, "all", options.All, "If true, select all resources in the namespace of the specified resource types") |
304a2b2b |
cmd.Flags().StringSliceVarP(&options.Filenames, "filename", "f", options.Filenames, "Filename, directory, or URL to file to use to edit the resource.")
cmd.Flags().BoolVar(&options.Remove, "remove", options.Remove, "If true, remove the specified trigger(s).")
cmd.Flags().BoolVar(&options.RemoveAll, "remove-all", options.RemoveAll, "If true, remove all triggers.") |
9c93da29 |
cmd.Flags().BoolVar(&options.Auto, "auto", options.Auto, "If true, enable all triggers, or just the specified trigger")
cmd.Flags().BoolVar(&options.Manual, "manual", options.Manual, "If true, set all triggers to manual, or just the specified trigger") |
304a2b2b |
cmd.Flags().BoolVar(&options.FromConfig, "from-config", options.FromConfig, "If set, configuration changes will result in a change")
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")
cmd.Flags().StringVar(&options.FromImage, "from-image", options.FromImage, "An image stream tag to trigger off of") |
9c93da29 |
options.FromGitHub = cmd.Flags().Bool("from-github", false, "If true, a GitHub webhook - a secret value will be generated automatically")
options.FromWebHook = cmd.Flags().Bool("from-webhook", false, "If true, a generic webhook - a secret value will be generated automatically")
options.FromWebHookAllowEnv = cmd.Flags().Bool("from-webhook-allow-env", false, "If true, a generic webhook which can provide environment variables - a secret value will be generated automatically") |
304a2b2b |
cmd.MarkFlagFilename("filename", "yaml", "yml", "json")
return cmd
}
func (o *TriggersOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, args []string) error {
cmdNamespace, explicit, err := f.DefaultNamespace()
if err != nil {
return err
}
|
5a556e67 |
clientConfig, err := f.ClientConfig()
if err != nil {
return err
}
o.OutputVersion, err = kcmdutil.OutputVersion(cmd, clientConfig.GroupVersion)
if err != nil {
return err
}
|
304a2b2b |
if !cmd.Flags().Lookup("from-github").Changed {
o.FromGitHub = nil
}
if !cmd.Flags().Lookup("from-webhook").Changed {
o.FromWebHook = nil
} |
150c7c58 |
if !cmd.Flags().Lookup("from-webhook-allow-env").Changed {
o.FromWebHookAllowEnv = nil
} |
304a2b2b |
if len(o.FromImage) > 0 {
ref, err := imageapi.ParseDockerImageReference(o.FromImage)
if err != nil {
return fmt.Errorf("the value of --from-image does not appear to be a valid reference to an image: %v", err)
}
if len(ref.Registry) > 0 || len(ref.ID) > 0 {
return fmt.Errorf("the value of --from-image must point to an image stream tag on this server")
}
if len(ref.Tag) == 0 {
return fmt.Errorf("the value of --from-image must include the tag you wish to pull from")
}
o.FromImage = ref.NameString()
o.FromImageNamespace = defaultNamespace(ref.Namespace, cmdNamespace)
}
count := o.count()
o.Reset = count == 0 && (o.Auto || o.Manual)
switch {
case count == 0 && !o.Remove && !o.RemoveAll && !o.Auto && !o.Manual:
o.PrintTable = true
case !o.RemoveAll && !o.Auto && !o.Manual:
o.Auto = true
}
|
59e6f9d2 |
mapper, typer := f.Object(false) |
304a2b2b |
o.Builder = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), kapi.Codecs.UniversalDecoder()).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). |
c5dc7f15 |
FilenameParam(explicit, false, o.Filenames...). |
304a2b2b |
SelectorParam(o.Selector).
ResourceTypeOrNameArgs(o.All, args...).
Flatten()
output := kcmdutil.GetFlagString(cmd, "output") |
0650f99d |
if len(output) > 0 {
o.PrintObject = func(infos []*resource.Info) error {
return f.PrintResourceInfos(cmd, infos, o.Out)
} |
304a2b2b |
}
o.Encoder = f.JSONEncoder()
o.ShortOutput = kcmdutil.GetFlagString(cmd, "output") == "name"
o.Mapper = mapper
return nil
}
func (o *TriggersOptions) count() int {
count := 0
if o.FromConfig {
count++
}
if o.FromGitHub != nil {
count++
}
if o.FromWebHook != nil {
count++
} |
150c7c58 |
if o.FromWebHookAllowEnv != nil {
count++
} |
304a2b2b |
if len(o.FromImage) > 0 {
count++
}
return count
}
func (o *TriggersOptions) Validate() error {
count := o.count()
switch {
case o.Auto && o.Manual:
return fmt.Errorf("you must specify at most one of --auto or --manual")
case o.Remove && o.RemoveAll:
return fmt.Errorf("you must specify either --remove or --remove-all")
case o.RemoveAll && (count != 0 || o.Auto || o.Manual):
return fmt.Errorf("--remove-all may not be used with any other flag")
case o.Remove && count < 1:
return fmt.Errorf("--remove requires a flag defining a trigger type to be specified")
case count > 1:
return fmt.Errorf("you may only set one trigger type at a time")
case count == 0 && !o.Remove && !o.RemoveAll && !o.Auto && !o.Manual && !o.PrintTable:
return fmt.Errorf("specify one of the --from-* flags to add a trigger, --remove to remove, or --auto|--manual to control existing triggers")
}
return nil
}
func (o *TriggersOptions) Run() error {
infos := o.Infos
singular := len(o.Infos) <= 1
if o.Builder != nil {
loaded, err := o.Builder.Do().IntoSingular(&singular).Infos()
if err != nil {
return err
}
infos = loaded
}
if o.PrintTable && o.PrintObject == nil {
return o.printTriggers(infos)
}
updateTriggerFn := func(triggers *TriggerDefinition) error {
o.updateTriggers(triggers)
return nil
}
patches := CalculatePatches(infos, o.Encoder, func(info *resource.Info) (bool, error) {
return UpdateTriggersForObject(info.Object, updateTriggerFn)
})
if singular && len(patches) == 0 {
return fmt.Errorf("%s/%s is not a deployment config or build config", infos[0].Mapping.Resource, infos[0].Name)
}
if o.PrintObject != nil { |
0650f99d |
return o.PrintObject(infos) |
304a2b2b |
}
failed := false
for _, patch := range patches {
info := patch.Info
if patch.Err != nil {
failed = true
fmt.Fprintf(o.Err, "error: %s/%s %v\n", info.Mapping.Resource, info.Name, patch.Err)
continue
}
if string(patch.Patch) == "{}" || len(patch.Patch) == 0 {
fmt.Fprintf(o.Err, "info: %s %q was not changed\n", info.Mapping.Resource, info.Name)
continue
}
glog.V(4).Infof("Calculated patch %s", patch.Patch)
obj, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, kapi.StrategicMergePatchType, patch.Patch)
if err != nil {
handlePodUpdateError(o.Err, err, "triggered")
failed = true
continue
}
info.Refresh(obj, true) |
7a2339f4 |
kcmdutil.PrintSuccess(o.Mapper, o.ShortOutput, o.Out, info.Mapping.Resource, info.Name, false, "updated") |
304a2b2b |
}
if failed {
return cmdutil.ErrExit
}
return nil
}
// printTriggers displays a tabular output of the triggers for each object.
func (o *TriggersOptions) printTriggers(infos []*resource.Info) error {
w := tabwriter.NewWriter(o.Out, 0, 2, 2, ' ', 0)
defer w.Flush()
fmt.Fprintf(w, "NAME\tTYPE\tVALUE\tAUTO\n")
for _, info := range infos {
_, err := UpdateTriggersForObject(info.Object, func(triggers *TriggerDefinition) error {
fmt.Fprintf(w, "%s/%s\t%s\t%s\t%t\n", info.Mapping.Resource, info.Name, "config", "", triggers.ConfigChange)
for _, image := range triggers.ImageChange {
var details string
switch {
case len(image.Names) > 0:
if len(image.Namespace) > 0 {
details = fmt.Sprintf("%s/%s (%s)", image.Namespace, image.From, strings.Join(image.Names, ", "))
} else {
details = fmt.Sprintf("%s (%s)", image.From, strings.Join(image.Names, ", "))
}
case len(image.Namespace) > 0:
details = fmt.Sprintf("%s/%s", image.Namespace, image.From)
default:
details = image.From
}
fmt.Fprintf(w, "%s/%s\t%s\t%s\t%t\n", info.Mapping.Resource, info.Name, "image", details, image.Auto)
}
for _, s := range triggers.WebHooks {
fmt.Fprintf(w, "%s/%s\t%s\t%s\t%s\n", info.Mapping.Resource, info.Name, "webhook", s, "")
}
for _, s := range triggers.GitHubWebHooks {
fmt.Fprintf(w, "%s/%s\t%s\t%s\t%s\n", info.Mapping.Resource, info.Name, "github", s, "")
}
return nil
})
if err != nil {
fmt.Fprintf(w, "%s/%s\t%s\t%s\t%t\n", info.Mapping.Resource, info.Name, "<error>", "", false)
}
}
return nil
}
// updateTriggers updates only those fields with flags set by the user
func (o *TriggersOptions) updateTriggers(triggers *TriggerDefinition) {
// clear everything
if o.RemoveAll {
*triggers = TriggerDefinition{}
return
}
// clear a specific field
if o.Remove {
if o.FromConfig {
triggers.ConfigChange = false
}
if len(o.FromImage) > 0 {
var newTriggers []ImageChangeTrigger
for _, trigger := range triggers.ImageChange {
if trigger.From != o.FromImage {
newTriggers = append(newTriggers, trigger)
}
}
triggers.ImageChange = newTriggers
}
if o.FromWebHook != nil && *o.FromWebHook {
triggers.WebHooks = nil
} |
150c7c58 |
if o.FromWebHookAllowEnv != nil && *o.FromWebHookAllowEnv {
triggers.WebHooks = nil
triggers.WebHooksAllowEnv = false
} |
304a2b2b |
if o.FromGitHub != nil && *o.FromGitHub {
triggers.GitHubWebHooks = nil
}
return
}
// change the automated status
if o.Reset {
triggers.ConfigChange = o.Auto
for i := range triggers.ImageChange {
triggers.ImageChange[i].Auto = o.Auto
}
return
}
// change individual elements
if o.FromConfig {
triggers.ConfigChange = true
}
if len(o.FromImage) > 0 {
names := strings.Split(o.ContainerNames, ",")
if len(o.ContainerNames) == 0 {
names = nil
}
found := false
for i, trigger := range triggers.ImageChange {
if trigger.From == o.FromImage && trigger.Namespace == o.FromImageNamespace {
found = true
triggers.ImageChange[i].Auto = !o.Manual
triggers.ImageChange[i].Names = names
break
}
}
if !found {
triggers.ImageChange = append(triggers.ImageChange, ImageChangeTrigger{
From: o.FromImage,
Namespace: o.FromImageNamespace,
Auto: !o.Manual,
Names: names,
})
}
}
if o.FromWebHook != nil && *o.FromWebHook {
triggers.WebHooks = []string{app.GenerateSecret(20)}
} |
150c7c58 |
if o.FromWebHookAllowEnv != nil && *o.FromWebHookAllowEnv {
triggers.WebHooks = []string{app.GenerateSecret(20)}
triggers.WebHooksAllowEnv = true
} |
304a2b2b |
if o.FromGitHub != nil && *o.FromGitHub {
triggers.GitHubWebHooks = []string{app.GenerateSecret(20)}
}
}
// ImageChangeTrigger represents the capabilities present in deployment config and build
// config objects in a consistent way.
type ImageChangeTrigger struct {
// If this trigger is automatically applied
Auto bool
// An ImageStreamTag name to target
From string
// The target namespace, normalized if set
Namespace string
// A list of names this trigger targets
Names []string
}
// TriggerDefinition is the abstract representation of triggers for builds and deploymnet configs.
type TriggerDefinition struct { |
150c7c58 |
ConfigChange bool
ImageChange []ImageChangeTrigger
WebHooks []string
WebHooksAllowEnv bool
GitHubWebHooks []string |
304a2b2b |
}
// defaultNamespace returns an empty string if the provided namespace matches the default namespace, or
// returns the namespace.
func defaultNamespace(namespace, defaultNamespace string) string {
if namespace == defaultNamespace {
return ""
}
return namespace
}
// NewDeploymentConfigTriggers creates a trigger definition from a deployment config.
func NewDeploymentConfigTriggers(config *deployapi.DeploymentConfig) *TriggerDefinition {
t := &TriggerDefinition{}
for _, trigger := range config.Spec.Triggers {
switch trigger.Type {
case deployapi.DeploymentTriggerOnConfigChange:
t.ConfigChange = true
case deployapi.DeploymentTriggerOnImageChange:
t.ImageChange = append(t.ImageChange, ImageChangeTrigger{
Auto: trigger.ImageChangeParams.Automatic,
Names: trigger.ImageChangeParams.ContainerNames,
From: trigger.ImageChangeParams.From.Name,
Namespace: defaultNamespace(trigger.ImageChangeParams.From.Namespace, config.Namespace),
})
}
}
return t
}
// NewBuildConfigTriggers creates a trigger definition from a build config.
func NewBuildConfigTriggers(config *buildapi.BuildConfig) *TriggerDefinition {
t := &TriggerDefinition{}
setStrategy := false
for _, trigger := range config.Spec.Triggers {
switch trigger.Type {
case buildapi.ConfigChangeBuildTriggerType:
t.ConfigChange = true
case buildapi.GenericWebHookBuildTriggerType:
t.WebHooks = append(t.WebHooks, trigger.GenericWebHook.Secret) |
150c7c58 |
t.WebHooksAllowEnv = trigger.GenericWebHook.AllowEnv |
304a2b2b |
case buildapi.GitHubWebHookBuildTriggerType:
t.GitHubWebHooks = append(t.GitHubWebHooks, trigger.GitHubWebHook.Secret)
case buildapi.ImageChangeBuildTriggerType:
if trigger.ImageChange.From == nil {
if strategyTrigger := strategyTrigger(config); strategyTrigger != nil {
setStrategy = true
strategyTrigger.Auto = true
t.ImageChange = append(t.ImageChange, *strategyTrigger)
}
continue
}
// normalize the trigger
trigger.ImageChange.From.Namespace = defaultNamespace(trigger.ImageChange.From.Namespace, config.Namespace)
t.ImageChange = append(t.ImageChange, ImageChangeTrigger{
Auto: true,
From: trigger.ImageChange.From.Name,
Namespace: trigger.ImageChange.From.Namespace,
})
}
}
if !setStrategy {
if strategyTrigger := strategyTrigger(config); strategyTrigger != nil {
t.ImageChange = append(t.ImageChange, *strategyTrigger)
}
}
return t
}
// Apply writes a trigger definition back to a build or deployment config.
func (t *TriggerDefinition) Apply(obj runtime.Object) error {
switch c := obj.(type) {
case *deployapi.DeploymentConfig:
if len(t.GitHubWebHooks) > 0 {
return fmt.Errorf("deployment configs do not support GitHub web hooks")
}
if len(t.WebHooks) > 0 {
return fmt.Errorf("deployment configs do not support web hooks")
}
existingTriggers := filterDeploymentTriggers(c.Spec.Triggers, deployapi.DeploymentTriggerOnConfigChange)
var triggers []deployapi.DeploymentTriggerPolicy
if t.ConfigChange {
triggers = append(triggers, deployapi.DeploymentTriggerPolicy{Type: deployapi.DeploymentTriggerOnConfigChange})
}
allNames := sets.NewString()
for _, container := range c.Spec.Template.Spec.Containers {
allNames.Insert(container.Name)
}
for _, trigger := range t.ImageChange {
if len(trigger.Names) == 0 {
return fmt.Errorf("you must specify --containers when setting --from-image")
}
if !allNames.HasAll(trigger.Names...) {
return fmt.Errorf(
"not all container names exist: %s (accepts: %s)",
strings.Join(sets.NewString(trigger.Names...).Difference(allNames).List(), ", "),
strings.Join(allNames.List(), ", "),
)
}
triggers = append(triggers, deployapi.DeploymentTriggerPolicy{
Type: deployapi.DeploymentTriggerOnImageChange,
ImageChangeParams: &deployapi.DeploymentTriggerImageChangeParams{
Automatic: trigger.Auto,
From: kapi.ObjectReference{
Kind: "ImageStreamTag",
Name: trigger.From,
},
ContainerNames: trigger.Names,
},
})
}
c.Spec.Triggers = mergeDeployTriggers(existingTriggers, triggers)
return nil
case *buildapi.BuildConfig:
var triggers []buildapi.BuildTriggerPolicy
if t.ConfigChange {
triggers = append(triggers, buildapi.BuildTriggerPolicy{Type: buildapi.ConfigChangeBuildTriggerType})
}
for _, trigger := range t.WebHooks {
triggers = append(triggers, buildapi.BuildTriggerPolicy{
Type: buildapi.GenericWebHookBuildTriggerType,
GenericWebHook: &buildapi.WebHookTrigger{ |
150c7c58 |
Secret: trigger,
AllowEnv: t.WebHooksAllowEnv, |
304a2b2b |
},
})
}
for _, trigger := range t.GitHubWebHooks {
triggers = append(triggers, buildapi.BuildTriggerPolicy{
Type: buildapi.GitHubWebHookBuildTriggerType,
GitHubWebHook: &buildapi.WebHookTrigger{
Secret: trigger,
},
})
}
// add new triggers, filter out any old triggers that match (if moving from automatic to manual),
// and then merge the old triggers and the new triggers to preserve fields like lastTriggeredImageID
existingTriggers := c.Spec.Triggers
strategyTrigger := strategyTrigger(c)
for _, trigger := range t.ImageChange {
change := &buildapi.ImageChangeTrigger{
From: &kapi.ObjectReference{
Kind: "ImageStreamTag",
Name: trigger.From,
Namespace: trigger.Namespace,
},
}
// use the canonical ImageChangeTrigger with nil From
strategyTrigger.Auto = trigger.Auto
if reflect.DeepEqual(strategyTrigger, &trigger) {
change.From = nil
}
// if this trigger is not automatic, then we need to remove it from the list of triggers
if !trigger.Auto {
existingTriggers = filterBuildImageTriggers(existingTriggers, trigger, strategyTrigger)
continue
}
triggers = append(triggers, buildapi.BuildTriggerPolicy{
Type: buildapi.ImageChangeBuildTriggerType,
ImageChange: change,
})
}
c.Spec.Triggers = mergeBuildTriggers(existingTriggers, triggers)
return nil
default:
return fmt.Errorf("the object is not a deployment config or build config")
}
}
// triggerMatchesBuildImageChange identifies whether the image change is equivalent to the trigger
func triggerMatchesBuildImageChange(trigger ImageChangeTrigger, strategyTrigger *ImageChangeTrigger, imageChange *buildapi.ImageChangeTrigger) bool {
if imageChange == nil {
return false
}
if imageChange.From == nil {
return strategyTrigger != nil && strategyTrigger.From == trigger.From && strategyTrigger.Namespace == trigger.Namespace
}
namespace := imageChange.From.Namespace
if strategyTrigger != nil {
namespace = defaultNamespace(namespace, strategyTrigger.Namespace)
}
return imageChange.From.Name == trigger.From && namespace == trigger.Namespace
}
// filterBuildImageTriggers return only triggers that do not match the provided ImageChangeTrigger. strategyTrigger may be provided
// if set to remove a BuildTriggerPolicy without a From (which points to the strategy)
func filterBuildImageTriggers(src []buildapi.BuildTriggerPolicy, trigger ImageChangeTrigger, strategyTrigger *ImageChangeTrigger) []buildapi.BuildTriggerPolicy {
var dst []buildapi.BuildTriggerPolicy
for i := range src {
if triggerMatchesBuildImageChange(trigger, strategyTrigger, src[i].ImageChange) {
continue
}
dst = append(dst, src[i])
}
return dst
}
// filterDeploymentTriggers returns only triggers that do not have one of the provided types.
func filterDeploymentTriggers(src []deployapi.DeploymentTriggerPolicy, types ...deployapi.DeploymentTriggerType) []deployapi.DeploymentTriggerPolicy {
var dst []deployapi.DeploymentTriggerPolicy
Outer:
for i := range src {
for _, t := range types {
if t == src[i].Type {
continue Outer
}
}
dst = append(dst, src[i])
}
return dst
}
// strategyTrigger returns a synthetic ImageChangeTrigger that represents the image stream tag the build strategy
// points to, or nil if no such strategy trigger is possible (if the build doesn't point to an ImageStreamTag).
func strategyTrigger(config *buildapi.BuildConfig) *ImageChangeTrigger { |
482a64f4 |
if from := buildutil.GetInputReference(config.Spec.Strategy); from != nil { |
304a2b2b |
if from.Kind == "ImageStreamTag" {
// normalize the strategy object reference
from.Namespace = defaultNamespace(from.Namespace, config.Namespace)
return &ImageChangeTrigger{From: from.Name, Namespace: from.Namespace}
}
}
return nil
}
// mergeDeployTriggers returns an array of DeploymentTriggerPolicies that have no duplicates.
func mergeDeployTriggers(dst, src []deployapi.DeploymentTriggerPolicy) []deployapi.DeploymentTriggerPolicy {
// never return an empty map, because the triggers on a deployment config default when the map is empty
result := []deployapi.DeploymentTriggerPolicy{}
for _, current := range dst {
if findDeployTrigger(src, current) != -1 {
result = append(result, current)
}
}
for _, current := range src {
if findDeployTrigger(result, current) == -1 {
result = append(result, current)
}
}
return result
}
// findDeployTrigger finds the position of a deployment trigger in the provided array, or -1 if no such
// matching trigger is found.
func findDeployTrigger(dst []deployapi.DeploymentTriggerPolicy, trigger deployapi.DeploymentTriggerPolicy) int {
for i := range dst {
if reflect.DeepEqual(dst[i], trigger) {
return i
}
}
return -1
}
// mergeBuildTriggers returns an array of BuildTriggerPolicies that have no duplicates, in the same order
// as they exist in their original arrays (a zip-merge).
func mergeBuildTriggers(dst, src []buildapi.BuildTriggerPolicy) []buildapi.BuildTriggerPolicy {
var result []buildapi.BuildTriggerPolicy
for _, current := range dst {
if findBuildTrigger(src, current) != -1 {
result = append(result, current)
}
}
for _, current := range src {
if findBuildTrigger(result, current) == -1 {
result = append(result, current)
}
}
return result
}
// findBuildTrigger finds the equivalent build trigger position in the provided array, or -1 if
// no such build trigger exists. Equality only cares about the value of the From field.
func findBuildTrigger(dst []buildapi.BuildTriggerPolicy, trigger buildapi.BuildTriggerPolicy) int {
// make a copy for semantic equality
if trigger.ImageChange != nil {
trigger.ImageChange = &buildapi.ImageChangeTrigger{From: trigger.ImageChange.From}
}
for i, copied := range dst {
// make a copy for semantic equality
if copied.ImageChange != nil {
copied.ImageChange = &buildapi.ImageChangeTrigger{From: copied.ImageChange.From}
}
if reflect.DeepEqual(copied, trigger) {
return i
}
}
return -1
}
// UpdateTriggersForObject extracts a trigger definition from the provided object, passes it to fn, and
// then applies the trigger definition back on the object. It returns true if the object was mutated
// and an optional error if the any part of the flow returns error.
func UpdateTriggersForObject(obj runtime.Object, fn func(*TriggerDefinition) error) (bool, error) {
// TODO: replace with a swagger schema based approach (identify pod template via schema introspection)
switch t := obj.(type) {
case *deployapi.DeploymentConfig:
triggers := NewDeploymentConfigTriggers(t)
if err := fn(triggers); err != nil {
return true, err
}
return true, triggers.Apply(t)
case *buildapi.BuildConfig:
triggers := NewBuildConfigTriggers(t)
if err := fn(triggers); err != nil {
return true, err
}
return true, triggers.Apply(t)
default:
return false, fmt.Errorf("the object is not a deployment config or build config")
}
} |