forked from LaconicNetwork/kompose
update vendor
added files required by BuildConfig reaper
This commit is contained in:
parent
bee784faf6
commit
166eedfc1c
2
vendor/github.com/openshift/origin/pkg/build/cmd/doc.go
generated
vendored
Normal file
2
vendor/github.com/openshift/origin/pkg/build/cmd/doc.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// Package cmd provides command helpers for builds
|
||||||
|
package cmd
|
||||||
150
vendor/github.com/openshift/origin/pkg/build/cmd/reaper.go
generated
vendored
Normal file
150
vendor/github.com/openshift/origin/pkg/build/cmd/reaper.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
||||||
4
vendor/github.com/openshift/origin/pkg/util/doc.go
generated
vendored
Normal file
4
vendor/github.com/openshift/origin/pkg/util/doc.go
generated
vendored
Normal file
@ -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
|
||||||
21
vendor/github.com/openshift/origin/pkg/util/etcd.go
generated
vendored
Normal file
21
vendor/github.com/openshift/origin/pkg/util/etcd.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
||||||
286
vendor/github.com/openshift/origin/pkg/util/labels.go
generated
vendored
Normal file
286
vendor/github.com/openshift/origin/pkg/util/labels.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
||||||
21
vendor/github.com/openshift/origin/pkg/util/strings.go
generated
vendored
Normal file
21
vendor/github.com/openshift/origin/pkg/util/strings.go
generated
vendored
Normal file
@ -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
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user