diff --git a/vendor/github.com/openshift/origin/pkg/build/cmd/doc.go b/vendor/github.com/openshift/origin/pkg/build/cmd/doc.go new file mode 100644 index 00000000..e9cc603a --- /dev/null +++ b/vendor/github.com/openshift/origin/pkg/build/cmd/doc.go @@ -0,0 +1,2 @@ +// Package cmd provides command helpers for builds +package cmd diff --git a/vendor/github.com/openshift/origin/pkg/build/cmd/reaper.go b/vendor/github.com/openshift/origin/pkg/build/cmd/reaper.go new file mode 100644 index 00000000..11fb11a5 --- /dev/null +++ b/vendor/github.com/openshift/origin/pkg/build/cmd/reaper.go @@ -0,0 +1,150 @@ +package cmd + +import ( + "sort" + "strings" + "time" + + "github.com/golang/glog" + kapi "k8s.io/kubernetes/pkg/api" + kerrors "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/client/unversioned" + "k8s.io/kubernetes/pkg/kubectl" + ktypes "k8s.io/kubernetes/pkg/types" + kutilerrors "k8s.io/kubernetes/pkg/util/errors" + + buildapi "github.com/openshift/origin/pkg/build/api" + buildutil "github.com/openshift/origin/pkg/build/util" + "github.com/openshift/origin/pkg/client" + "github.com/openshift/origin/pkg/util" +) + +// NewBuildConfigReaper returns a new reaper for buildConfigs +func NewBuildConfigReaper(oc *client.Client) kubectl.Reaper { + return &BuildConfigReaper{oc: oc, pollInterval: kubectl.Interval, timeout: kubectl.Timeout} +} + +// BuildConfigReaper implements the Reaper interface for buildConfigs +type BuildConfigReaper struct { + oc client.Interface + pollInterval, timeout time.Duration +} + +// Stop deletes the build configuration and all of the associated builds. +func (reaper *BuildConfigReaper) Stop(namespace, name string, timeout time.Duration, gracePeriod *kapi.DeleteOptions) error { + _, err := reaper.oc.BuildConfigs(namespace).Get(name) + + if err != nil { + return err + } + + var bcPotentialBuilds []buildapi.Build + + // Collect builds related to the config. + builds, err := reaper.oc.Builds(namespace).List(kapi.ListOptions{LabelSelector: buildutil.BuildConfigSelector(name)}) + if err != nil { + return err + } + + bcPotentialBuilds = append(bcPotentialBuilds, builds.Items...) + + // Collect deprecated builds related to the config. + // TODO: Delete this block after BuildConfigLabelDeprecated is removed. + builds, err = reaper.oc.Builds(namespace).List(kapi.ListOptions{LabelSelector: buildutil.BuildConfigSelectorDeprecated(name)}) + if err != nil { + return err + } + + bcPotentialBuilds = append(bcPotentialBuilds, builds.Items...) + + // A map of builds associated with this build configuration + bcBuilds := make(map[ktypes.UID]buildapi.Build) + + // Because of name length limits in the BuildConfigSelector, annotations are used to ensure + // reliable selection of associated builds. + for _, build := range bcPotentialBuilds { + if build.Annotations != nil { + if bcName, ok := build.Annotations[buildapi.BuildConfigAnnotation]; ok { + // The annotation, if present, has the full build config name. + if bcName != name { + // If the name does not match exactly, the build is not truly associated with the build configuration + continue + } + } + } + // Note that if there is no annotation, this is a deprecated build spec + // and we choose to include it in the deletion having matched only the BuildConfigSelectorDeprecated + + // Use a map to union the lists returned by the contemporary & deprecated build queries + // (there will be overlap between the lists, and we only want to try to delete each build once) + bcBuilds[build.UID] = build + } + + // If there are builds associated with this build configuration, pause it before attempting the deletion + if len(bcBuilds) > 0 { + + // Add paused annotation to the build config pending the deletion + err = unversioned.RetryOnConflict(unversioned.DefaultRetry, func() error { + + bc, err := reaper.oc.BuildConfigs(namespace).Get(name) + if err != nil { + return err + } + + // Ignore if the annotation already exists + if strings.ToLower(bc.Annotations[buildapi.BuildConfigPausedAnnotation]) == "true" { + return nil + } + + // Set the annotation and update + if err := util.AddObjectAnnotations(bc, map[string]string{buildapi.BuildConfigPausedAnnotation: "true"}); err != nil { + return err + } + _, err = reaper.oc.BuildConfigs(namespace).Update(bc) + return err + }) + + if err != nil { + return err + } + + } + + // Warn the user if the BuildConfig won't get deleted after this point. + bcDeleted := false + defer func() { + if !bcDeleted { + glog.Warningf("BuildConfig %s/%s will not be deleted because not all associated builds could be deleted. You can try re-running the command or removing them manually", namespace, name) + } + }() + + // For the benefit of test cases, sort the UIDs so that the deletion order is deterministic + buildUIDs := make([]string, 0, len(bcBuilds)) + for buildUID := range bcBuilds { + buildUIDs = append(buildUIDs, string(buildUID)) + } + sort.Strings(buildUIDs) + + errList := []error{} + for _, buildUID := range buildUIDs { + build := bcBuilds[ktypes.UID(buildUID)] + if err := reaper.oc.Builds(namespace).Delete(build.Name); err != nil { + glog.Warningf("Cannot delete Build %s/%s: %v", build.Namespace, build.Name, err) + if !kerrors.IsNotFound(err) { + errList = append(errList, err) + } + } + } + + // Aggregate all errors + if len(errList) > 0 { + return kutilerrors.NewAggregate(errList) + } + + if err := reaper.oc.BuildConfigs(namespace).Delete(name); err != nil { + return err + } + + bcDeleted = true + return nil +} diff --git a/vendor/github.com/openshift/origin/pkg/util/doc.go b/vendor/github.com/openshift/origin/pkg/util/doc.go new file mode 100644 index 00000000..3ce2be72 --- /dev/null +++ b/vendor/github.com/openshift/origin/pkg/util/doc.go @@ -0,0 +1,4 @@ +// Package util implements various utility functions used in both testing and +// implementation of OpenShift. Package util may not depend on any other +// package in the OpenShift package tree. +package util diff --git a/vendor/github.com/openshift/origin/pkg/util/etcd.go b/vendor/github.com/openshift/origin/pkg/util/etcd.go new file mode 100644 index 00000000..754f49d9 --- /dev/null +++ b/vendor/github.com/openshift/origin/pkg/util/etcd.go @@ -0,0 +1,21 @@ +package util + +import ( + "path" + + kapi "k8s.io/kubernetes/pkg/api" + kerrors "k8s.io/kubernetes/pkg/api/errors" +) + +// NoNamespaceKeyFunc is the default function for constructing etcd paths to a resource relative to prefix enforcing +// If a namespace is on context, it errors. +func NoNamespaceKeyFunc(ctx kapi.Context, prefix string, name string) (string, error) { + ns, ok := kapi.NamespaceFrom(ctx) + if ok && len(ns) > 0 { + return "", kerrors.NewBadRequest("Namespace parameter is not allowed.") + } + if len(name) == 0 { + return "", kerrors.NewBadRequest("Name parameter required.") + } + return path.Join(prefix, name), nil +} diff --git a/vendor/github.com/openshift/origin/pkg/util/labels.go b/vendor/github.com/openshift/origin/pkg/util/labels.go new file mode 100644 index 00000000..eba90666 --- /dev/null +++ b/vendor/github.com/openshift/origin/pkg/util/labels.go @@ -0,0 +1,286 @@ +package util + +import ( + "fmt" + "reflect" + + kmeta "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/labels" + "k8s.io/kubernetes/pkg/runtime" + + deployapi "github.com/openshift/origin/pkg/deploy/api" +) + +// MergeInto flags +const ( + OverwriteExistingDstKey = 1 << iota + ErrorOnExistingDstKey + ErrorOnDifferentDstKeyValue +) + +// AddObjectLabelsWithFlags will set labels on the target object. Label overwrite behavior +// is controlled by the flags argument. +func AddObjectLabelsWithFlags(obj runtime.Object, labels labels.Set, flags int) error { + if labels == nil { + return nil + } + + accessor, err := kmeta.Accessor(obj) + + if err != nil { + if _, ok := obj.(*runtime.Unstructured); !ok { + // error out if it's not possible to get an accessor and it's also not an unstructured object + return err + } + } else { + metaLabels := accessor.GetLabels() + if metaLabels == nil { + metaLabels = make(map[string]string) + } + + switch objType := obj.(type) { + case *deployapi.DeploymentConfig: + if err := addDeploymentConfigNestedLabels(objType, labels, flags); err != nil { + return fmt.Errorf("unable to add nested labels to %s/%s: %v", obj.GetObjectKind().GroupVersionKind(), accessor.GetName(), err) + } + } + + if err := MergeInto(metaLabels, labels, flags); err != nil { + return fmt.Errorf("unable to add labels to %s/%s: %v", obj.GetObjectKind().GroupVersionKind(), accessor.GetName(), err) + } + + accessor.SetLabels(metaLabels) + + return nil + } + + // handle unstructured object + // TODO: allow meta.Accessor to handle runtime.Unstructured + if unstruct, ok := obj.(*runtime.Unstructured); ok && unstruct.Object != nil { + // the presence of "metadata" is sufficient for us to apply the rules for Kube-like + // objects. + // TODO: add swagger detection to allow this to happen more effectively + if obj, ok := unstruct.Object["metadata"]; ok { + if m, ok := obj.(map[string]interface{}); ok { + + existing := make(map[string]string) + if l, ok := m["labels"]; ok { + if found, ok := interfaceToStringMap(l); ok { + existing = found + } + } + if err := MergeInto(existing, labels, flags); err != nil { + return err + } + m["labels"] = mapToGeneric(existing) + } + return nil + } + + // only attempt to set root labels if a root object called labels exists + // TODO: add swagger detection to allow this to happen more effectively + if obj, ok := unstruct.Object["labels"]; ok { + existing := make(map[string]string) + if found, ok := interfaceToStringMap(obj); ok { + existing = found + } + if err := MergeInto(existing, labels, flags); err != nil { + return err + } + unstruct.Object["labels"] = mapToGeneric(existing) + return nil + } + } + + return nil + +} + +// AddObjectLabels adds new label(s) to a single runtime.Object, overwriting +// existing labels that have the same key. +func AddObjectLabels(obj runtime.Object, labels labels.Set) error { + return AddObjectLabelsWithFlags(obj, labels, OverwriteExistingDstKey) +} + +// AddObjectAnnotations adds new annotation(s) to a single runtime.Object +func AddObjectAnnotations(obj runtime.Object, annotations map[string]string) error { + if len(annotations) == 0 { + return nil + } + + accessor, err := kmeta.Accessor(obj) + + if err != nil { + if _, ok := obj.(*runtime.Unstructured); !ok { + // error out if it's not possible to get an accessor and it's also not an unstructured object + return err + } + } else { + metaAnnotations := accessor.GetAnnotations() + if metaAnnotations == nil { + metaAnnotations = make(map[string]string) + } + + switch objType := obj.(type) { + case *deployapi.DeploymentConfig: + if err := addDeploymentConfigNestedAnnotations(objType, annotations); err != nil { + return fmt.Errorf("unable to add nested annotations to %s/%s: %v", obj.GetObjectKind().GroupVersionKind(), accessor.GetName(), err) + } + } + + MergeInto(metaAnnotations, annotations, OverwriteExistingDstKey) + accessor.SetAnnotations(metaAnnotations) + + return nil + } + + // handle unstructured object + // TODO: allow meta.Accessor to handle runtime.Unstructured + if unstruct, ok := obj.(*runtime.Unstructured); ok && unstruct.Object != nil { + // the presence of "metadata" is sufficient for us to apply the rules for Kube-like + // objects. + // TODO: add swagger detection to allow this to happen more effectively + if obj, ok := unstruct.Object["metadata"]; ok { + if m, ok := obj.(map[string]interface{}); ok { + + existing := make(map[string]string) + if l, ok := m["annotations"]; ok { + if found, ok := interfaceToStringMap(l); ok { + existing = found + } + } + if err := MergeInto(existing, annotations, OverwriteExistingDstKey); err != nil { + return err + } + m["annotations"] = mapToGeneric(existing) + } + return nil + } + + // only attempt to set root annotations if a root object called annotations exists + // TODO: add swagger detection to allow this to happen more effectively + if obj, ok := unstruct.Object["annotations"]; ok { + existing := make(map[string]string) + if found, ok := interfaceToStringMap(obj); ok { + existing = found + } + if err := MergeInto(existing, annotations, OverwriteExistingDstKey); err != nil { + return err + } + unstruct.Object["annotations"] = mapToGeneric(existing) + return nil + } + } + + return nil +} + +// addDeploymentConfigNestedLabels adds new label(s) to a nested labels of a single DeploymentConfig object +func addDeploymentConfigNestedLabels(obj *deployapi.DeploymentConfig, labels labels.Set, flags int) error { + if obj.Spec.Template.Labels == nil { + obj.Spec.Template.Labels = make(map[string]string) + } + if err := MergeInto(obj.Spec.Template.Labels, labels, flags); err != nil { + return fmt.Errorf("unable to add labels to Template.DeploymentConfig.Template.ControllerTemplate.Template: %v", err) + } + return nil +} + +func addDeploymentConfigNestedAnnotations(obj *deployapi.DeploymentConfig, annotations map[string]string) error { + if obj.Spec.Template == nil { + return nil + } + + if obj.Spec.Template.Annotations == nil { + obj.Spec.Template.Annotations = make(map[string]string) + } + + if err := MergeInto(obj.Spec.Template.Annotations, annotations, OverwriteExistingDstKey); err != nil { + return fmt.Errorf("unable to add annotations to Template.DeploymentConfig.Template.ControllerTemplate.Template: %v", err) + } + return nil +} + +// interfaceToStringMap extracts a map[string]string from a map[string]interface{} +func interfaceToStringMap(obj interface{}) (map[string]string, bool) { + if obj == nil { + return nil, false + } + lm, ok := obj.(map[string]interface{}) + if !ok { + return nil, false + } + existing := make(map[string]string) + for k, v := range lm { + switch t := v.(type) { + case string: + existing[k] = t + } + } + return existing, true +} + +// mapToGeneric converts a map[string]string into a map[string]interface{} +func mapToGeneric(obj map[string]string) map[string]interface{} { + if obj == nil { + return nil + } + res := make(map[string]interface{}) + for k, v := range obj { + res[k] = v + } + return res +} + +// MergeInto merges items from a src map into a dst map. +// Returns an error when the maps are not of the same type. +// Flags: +// - ErrorOnExistingDstKey +// When set: Return an error if any of the dst keys is already set. +// - ErrorOnDifferentDstKeyValue +// When set: Return an error if any of the dst keys is already set +// to a different value than src key. +// - OverwriteDstKey +// When set: Overwrite existing dst key value with src key value. +func MergeInto(dst, src interface{}, flags int) error { + dstVal := reflect.ValueOf(dst) + srcVal := reflect.ValueOf(src) + + if dstVal.Kind() != reflect.Map { + return fmt.Errorf("dst is not a valid map: %v", dstVal.Kind()) + } + if srcVal.Kind() != reflect.Map { + return fmt.Errorf("src is not a valid map: %v", srcVal.Kind()) + } + if dstTyp, srcTyp := dstVal.Type(), srcVal.Type(); !dstTyp.AssignableTo(srcTyp) { + return fmt.Errorf("type mismatch, can't assign '%v' to '%v'", srcTyp, dstTyp) + } + + if dstVal.IsNil() { + return fmt.Errorf("dst value is nil") + } + if srcVal.IsNil() { + // Nothing to merge + return nil + } + + for _, k := range srcVal.MapKeys() { + if dstVal.MapIndex(k).IsValid() { + if flags&ErrorOnExistingDstKey != 0 { + return fmt.Errorf("dst key already set (ErrorOnExistingDstKey=1), '%v'='%v'", k, dstVal.MapIndex(k)) + } + if dstVal.MapIndex(k).String() != srcVal.MapIndex(k).String() { + if flags&ErrorOnDifferentDstKeyValue != 0 { + return fmt.Errorf("dst key already set to a different value (ErrorOnDifferentDstKeyValue=1), '%v'='%v'", k, dstVal.MapIndex(k)) + } + if flags&OverwriteExistingDstKey != 0 { + dstVal.SetMapIndex(k, srcVal.MapIndex(k)) + } + } + } else { + dstVal.SetMapIndex(k, srcVal.MapIndex(k)) + } + } + + return nil +} diff --git a/vendor/github.com/openshift/origin/pkg/util/strings.go b/vendor/github.com/openshift/origin/pkg/util/strings.go new file mode 100644 index 00000000..cf1b4804 --- /dev/null +++ b/vendor/github.com/openshift/origin/pkg/util/strings.go @@ -0,0 +1,21 @@ +package util + +import "sort" + +// UniqueStrings returns a sorted, uniquified slice of the specified strings +func UniqueStrings(strings []string) []string { + m := make(map[string]bool, len(strings)) + for _, s := range strings { + m[s] = true + } + + i := 0 + strings = make([]string, len(m), len(m)) + for s := range m { + strings[i] = s + i++ + } + + sort.Strings(strings) + return strings +}