diff --git a/glide.lock b/glide.lock index 3563f949d..56e520633 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: 524d47ba2b9ae54115ade7c896a4fb9ca17c77d3db633fe10daf87a9c35f8df7 -updated: 2018-01-29T08:35:27.263987742-08:00 +updated: 2018-01-29T22:07:06.837426641-08:00 imports: - name: cloud.google.com/go version: fe3d41e1ecb2ce36ad3a979037c9b9a2b726226f @@ -29,8 +29,12 @@ imports: - name: github.com/appscode/kutil version: a035b515d68bca793b2beacdf52e19a02f914017 subpackages: + - core/v1 - meta + - rbac/v1beta1 - tools/analytics +- name: github.com/appscode/mergo + version: e3000cb3d28c72b837601cac94debd91032d19fe - name: github.com/appscode/osm version: 8175dd853f6f3d4530b14018901c79140e305f3a subpackages: @@ -439,7 +443,7 @@ imports: - unicode/norm - width - name: google.golang.org/api - version: d7238a695cdc29affa1e76d5485eb2f72d30f8f8 + version: 7d0e2d350555821bef5a5b8aecf0d12cc1def633 subpackages: - gensupport - googleapi diff --git a/pkg/cmds/init.go b/pkg/cmds/init.go index 05fa5b831..d770c917f 100644 --- a/pkg/cmds/init.go +++ b/pkg/cmds/init.go @@ -223,6 +223,15 @@ func updateOperatorDeployment(cmd *cobra.Command, out, errOut io.Writer) error { return err } + if configureRBAC { + if err := EnsureRBACStuff(client, namespace, out); err != nil { + return err + } + deployment.Spec.Template.Spec.ServiceAccountName = ServiceAccountName + } else { + deployment.Spec.Template.Spec.ServiceAccountName = "" + } + containers := deployment.Spec.Template.Spec.Containers if len(containers) == 0 { fmt.Fprintln(errOut, fmt.Sprintf(`Invalid operator deployment "%v"`, operatorName)) @@ -248,15 +257,6 @@ func updateOperatorDeployment(cmd *cobra.Command, out, errOut io.Writer) error { deployment.Spec.Template.Spec.Containers[0].Image = fmt.Sprintf("%v:%v", repository, version) - if configureRBAC { - if err := EnsureRBACStuff(client, namespace, out); err != nil { - return err - } - deployment.Spec.Template.Spec.ServiceAccountName = ServiceAccountName - } else { - deployment.Spec.Template.Spec.ServiceAccountName = "" - } - deployment.Spec.Template.Spec.Containers[0].Args = []string{ "run", fmt.Sprintf("--governing-service=%v", governingService), diff --git a/pkg/cmds/role.go b/pkg/cmds/role.go index 748b0fd43..15dd3ab89 100644 --- a/pkg/cmds/role.go +++ b/pkg/cmds/role.go @@ -4,15 +4,16 @@ import ( "fmt" "io" + "github.com/appscode/kutil" + core_util "github.com/appscode/kutil/core/v1" + rbac_util "github.com/appscode/kutil/rbac/v1beta1" "github.com/kubedb/apimachinery/apis/kubedb" apps "k8s.io/api/apps/v1beta1" batch "k8s.io/api/batch/v1" core "k8s.io/api/core/v1" - extensions "k8s.io/api/extensions/v1beta1" rbac "k8s.io/api/rbac/v1beta1" storage "k8s.io/api/storage/v1" apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - kerr "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" ) @@ -25,11 +26,6 @@ var policyRuleOperator = []rbac.PolicyRule{ Resources: []string{"customresourcedefinitions"}, Verbs: []string{"create", "delete", "get", "list"}, }, - { - APIGroups: []string{extensions.GroupName}, - Resources: []string{"thirdpartyresources"}, - Verbs: []string{"create", "delete", "get", "list"}, - }, { APIGroups: []string{rbac.GroupName}, Resources: []string{"rolebindings", "roles"}, @@ -43,7 +39,7 @@ var policyRuleOperator = []rbac.PolicyRule{ { APIGroups: []string{core.GroupName}, Resources: []string{"secrets", "serviceaccounts"}, - Verbs: []string{"create", "delete", "get"}, + Verbs: []string{"create", "delete", "get", "patch"}, }, { APIGroups: []string{apps.GroupName}, @@ -53,7 +49,7 @@ var policyRuleOperator = []rbac.PolicyRule{ { APIGroups: []string{batch.GroupName}, Resources: []string{"jobs"}, - Verbs: []string{"create", "delete", "get"}, + Verbs: []string{"create", "delete", "get", "list", "watch"}, }, { APIGroups: []string{storage.GroupName}, @@ -68,7 +64,7 @@ var policyRuleOperator = []rbac.PolicyRule{ { APIGroups: []string{core.GroupName}, Resources: []string{"persistentvolumeclaims"}, - Verbs: []string{"delete", "get", "list", "watch"}, + Verbs: []string{"delete", "get", "list", "patch", "watch"}, }, { APIGroups: []string{core.GroupName}, @@ -98,50 +94,46 @@ var policyRuleOperator = []rbac.PolicyRule{ } func EnsureRBACStuff(client kubernetes.Interface, namespace string, out io.Writer) error { + name := ServiceAccountName + // Ensure ClusterRoles for operator - clusterRoleOperator, err := client.RbacV1beta1().ClusterRoles().Get(name, metav1.GetOptions{}) + cr, vt1, err := rbac_util.CreateOrPatchClusterRole( + client, + metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + func(in *rbac.ClusterRole) *rbac.ClusterRole { + in.Labels = core_util.UpsertMap(in.Labels, operatorLabel) + in.Rules = policyRuleOperator + return in + }, + ) if err != nil { - if !kerr.IsNotFound(err) { - return err - } - // Create new one - role := &rbac.ClusterRole{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: operatorLabel, - }, - Rules: policyRuleOperator, - } - if _, err := client.RbacV1beta1().ClusterRoles().Create(role); err != nil { - return err - } - fmt.Fprintln(out, "Successfully created cluster role.") - } else { - // Update existing one - clusterRoleOperator.Rules = policyRuleOperator - if _, err := client.RbacV1beta1().ClusterRoles().Update(clusterRoleOperator); err != nil { - return err - } - fmt.Fprintln(out, "Successfully updated cluster role.") + return err + } + if vt1 != kutil.VerbUnchanged { + fmt.Fprintf(out, `ClusterRole "%s" successfully %v`, cr.Name, vt1) } // Ensure ServiceAccounts - if _, err := client.CoreV1().ServiceAccounts(namespace).Get(name, metav1.GetOptions{}); err != nil { - if !kerr.IsNotFound(err) { - return err - } - sa := &core.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: operatorLabel, - }, - } - if _, err := client.CoreV1().ServiceAccounts(namespace).Create(sa); err != nil { - return err - } - fmt.Fprintln(out, "Successfully created service account.") + sa, vt2, err := core_util.CreateOrPatchServiceAccount( + client, + metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + func(in *core.ServiceAccount) *core.ServiceAccount { + in.Labels = core_util.UpsertMap(in.Labels, operatorLabel) + return in + }, + ) + if err != nil { + return err + } + if vt2 != kutil.VerbUnchanged { + fmt.Fprintf(out, `ServiceAccount "%s" successfully %v`, sa.Name, vt2) } var roleBindingRef = rbac.RoleRef{ @@ -158,34 +150,25 @@ func EnsureRBACStuff(client kubernetes.Interface, namespace string, out io.Write } // Ensure ClusterRoleBindings - roleBinding, err := client.RbacV1beta1().ClusterRoleBindings().Get(name, metav1.GetOptions{}) - if err != nil { - if !kerr.IsNotFound(err) { - return err - } - - roleBinding := &rbac.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: operatorLabel, - }, - RoleRef: roleBindingRef, - Subjects: roleBindingSubjects, - } + crb, vt3, err := rbac_util.CreateOrPatchClusterRoleBinding( + client, + metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + }, + func(in *rbac.ClusterRoleBinding) *rbac.ClusterRoleBinding { + in.Labels = core_util.UpsertMap(in.Labels, operatorLabel) + in.RoleRef = roleBindingRef + in.Subjects = roleBindingSubjects - if _, err := client.RbacV1beta1().ClusterRoleBindings().Create(roleBinding); err != nil { - return err - } - fmt.Fprintln(out, "Successfully created cluster role bindings.") - } else { - roleBinding.RoleRef = roleBindingRef - roleBinding.Subjects = roleBindingSubjects - if _, err := client.RbacV1beta1().ClusterRoleBindings().Update(roleBinding); err != nil { - return err - } - fmt.Fprintln(out, "Successfully updated cluster role bindings.") + return in + }, + ) + if err != nil { + return err + } + if vt3 != kutil.VerbUnchanged { + fmt.Fprintf(out, `ClusterRoleBinding "%s" successfully %v`, crb.Name, vt3) } - return nil } diff --git a/vendor/github.com/appscode/kutil/core/v1/configmap.go b/vendor/github.com/appscode/kutil/core/v1/configmap.go new file mode 100644 index 000000000..1142a43bc --- /dev/null +++ b/vendor/github.com/appscode/kutil/core/v1/configmap.go @@ -0,0 +1,78 @@ +package v1 + +import ( + "encoding/json" + "fmt" + + "github.com/appscode/kutil" + "github.com/golang/glog" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" +) + +func CreateOrPatchConfigMap(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.ConfigMap) *core.ConfigMap) (*core.ConfigMap, kutil.VerbType, error) { + cur, err := c.CoreV1().ConfigMaps(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + glog.V(3).Infof("Creating ConfigMap %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().ConfigMaps(meta.Namespace).Create(transform(&core.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + })) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchConfigMap(c, cur, transform) +} + +func PatchConfigMap(c kubernetes.Interface, cur *core.ConfigMap, transform func(*core.ConfigMap) *core.ConfigMap) (*core.ConfigMap, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(transform(cur.DeepCopy())) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.ConfigMap{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + glog.V(3).Infof("Patching ConfigMap %s/%s with %s", cur.Namespace, cur.Name, string(patch)) + out, err := c.CoreV1().ConfigMaps(cur.Namespace).Patch(cur.Name, types.StrategicMergePatchType, patch) + return out, kutil.VerbPatched, err +} + +func TryUpdateConfigMap(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.ConfigMap) *core.ConfigMap) (result *core.ConfigMap, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().ConfigMaps(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().ConfigMaps(cur.Namespace).Update(transform(cur.DeepCopy())) + return e2 == nil, nil + } + glog.Errorf("Attempt %d failed to update ConfigMap %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = fmt.Errorf("failed to update ConfigMap %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} diff --git a/vendor/github.com/appscode/kutil/core/v1/kubernetes.go b/vendor/github.com/appscode/kutil/core/v1/kubernetes.go new file mode 100644 index 000000000..eb4e59e43 --- /dev/null +++ b/vendor/github.com/appscode/kutil/core/v1/kubernetes.go @@ -0,0 +1,271 @@ +package v1 + +import ( + "errors" + + "github.com/appscode/go/types" + "github.com/appscode/kutil/meta" + "github.com/appscode/mergo" + core "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/conversion" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func GetGroupVersionKind(v interface{}) schema.GroupVersionKind { + return core.SchemeGroupVersion.WithKind(meta.GetKind(v)) +} + +func AssignTypeKind(v interface{}) error { + _, err := conversion.EnforcePtr(v) + if err != nil { + return err + } + + switch u := v.(type) { + case *core.Pod: + u.APIVersion = core.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *core.ReplicationController: + u.APIVersion = core.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *core.ConfigMap: + u.APIVersion = core.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *core.Secret: + u.APIVersion = core.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *core.Service: + u.APIVersion = core.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *core.PersistentVolumeClaim: + u.APIVersion = core.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *core.PersistentVolume: + u.APIVersion = core.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *core.Node: + u.APIVersion = core.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *core.ServiceAccount: + u.APIVersion = core.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *core.Namespace: + u.APIVersion = core.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *core.Endpoints: + u.APIVersion = core.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *core.ComponentStatus: + u.APIVersion = core.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *core.LimitRange: + u.APIVersion = core.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *core.Event: + u.APIVersion = core.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + } + return errors.New("unknown api object type") +} + +func RemoveNextInitializer(m metav1.ObjectMeta) metav1.ObjectMeta { + if m.GetInitializers() != nil { + pendingInitializers := m.GetInitializers().Pending + // Remove self from the list of pending Initializers while preserving ordering. + if len(pendingInitializers) == 1 { + m.Initializers = nil + } else { + m.Initializers.Pending = append(pendingInitializers[:0], pendingInitializers[1:]...) + } + } + return m +} + +func AddFinalizer(m metav1.ObjectMeta, finalizer string) metav1.ObjectMeta { + for _, name := range m.Finalizers { + if name == finalizer { + return m + } + } + m.Finalizers = append(m.Finalizers, finalizer) + return m +} + +func HasFinalizer(m metav1.ObjectMeta, finalizer string) bool { + for _, name := range m.Finalizers { + if name == finalizer { + return true + } + } + return false +} + +func RemoveFinalizer(m metav1.ObjectMeta, finalizer string) metav1.ObjectMeta { + // https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating + r := m.Finalizers[:0] + for _, name := range m.Finalizers { + if name != finalizer { + r = append(r, name) + } + } + m.Finalizers = r + return m +} + +func EnsureContainerDeleted(containers []core.Container, name string) []core.Container { + for i, c := range containers { + if c.Name == name { + return append(containers[:i], containers[i+1:]...) + } + } + return containers +} + +func UpsertContainer(containers []core.Container, upsert core.Container) []core.Container { + for i, container := range containers { + if container.Name == upsert.Name { + err := mergo.MergeWithOverwrite(&container, upsert) + if err != nil { + panic(err) + } + containers[i] = container + return containers + } + } + return append(containers, upsert) +} + +func UpsertVolume(volumes []core.Volume, nv core.Volume) []core.Volume { + for i, vol := range volumes { + if vol.Name == nv.Name { + volumes[i] = nv + return volumes + } + } + return append(volumes, nv) +} + +func UpsertVolumeClaim(volumeClaims []core.PersistentVolumeClaim, upsert core.PersistentVolumeClaim) []core.PersistentVolumeClaim { + for i, vc := range volumeClaims { + if vc.Name == upsert.Name { + volumeClaims[i] = upsert + return volumeClaims + } + } + return append(volumeClaims, upsert) +} + +func EnsureVolumeDeleted(volumes []core.Volume, name string) []core.Volume { + for i, v := range volumes { + if v.Name == name { + return append(volumes[:i], volumes[i+1:]...) + } + } + return volumes +} + +func UpsertVolumeMount(mounts []core.VolumeMount, nv core.VolumeMount) []core.VolumeMount { + for i, vol := range mounts { + if vol.Name == nv.Name { + mounts[i] = nv + return mounts + } + } + return append(mounts, nv) +} + +func EnsureVolumeMountDeleted(mounts []core.VolumeMount, name string) []core.VolumeMount { + for i, v := range mounts { + if v.Name == name { + return append(mounts[:i], mounts[i+1:]...) + } + } + return mounts +} + +func UpsertEnvVars(vars []core.EnvVar, nv ...core.EnvVar) []core.EnvVar { + upsert := func(env core.EnvVar) { + for i, v := range vars { + if v.Name == env.Name { + vars[i] = env + return + } + } + vars = append(vars, env) + } + + for _, env := range nv { + upsert(env) + } + return vars +} + +func EnsureEnvVarDeleted(vars []core.EnvVar, name string) []core.EnvVar { + for i, v := range vars { + if v.Name == name { + return append(vars[:i], vars[i+1:]...) + } + } + return vars +} + +func UpsertMap(maps, upsert map[string]string) map[string]string { + if maps == nil { + maps = make(map[string]string) + } + for k, v := range upsert { + maps[k] = v + } + return maps +} + +func MergeLocalObjectReferences(old, new []core.LocalObjectReference) []core.LocalObjectReference { + m := make(map[string]core.LocalObjectReference) + for _, ref := range old { + m[ref.Name] = ref + } + for _, ref := range new { + m[ref.Name] = ref + } + + result := make([]core.LocalObjectReference, 0, len(m)) + for _, ref := range m { + result = append(result, ref) + } + return result +} + +func EnsureOwnerReference(meta metav1.ObjectMeta, owner *core.ObjectReference) metav1.ObjectMeta { + fi := -1 + for i, ref := range meta.OwnerReferences { + if ref.Kind == owner.Kind && ref.Name == owner.Name { + fi = i + break + } + } + if fi == -1 { + meta.OwnerReferences = append(meta.OwnerReferences, metav1.OwnerReference{}) + fi = len(meta.OwnerReferences) - 1 + } + meta.OwnerReferences[fi].APIVersion = owner.APIVersion + meta.OwnerReferences[fi].Kind = owner.Kind + meta.OwnerReferences[fi].Name = owner.Name + meta.OwnerReferences[fi].UID = owner.UID + meta.OwnerReferences[fi].BlockOwnerDeletion = types.TrueP() + return meta +} diff --git a/vendor/github.com/appscode/kutil/core/v1/node.go b/vendor/github.com/appscode/kutil/core/v1/node.go new file mode 100644 index 000000000..594b80359 --- /dev/null +++ b/vendor/github.com/appscode/kutil/core/v1/node.go @@ -0,0 +1,96 @@ +package v1 + +import ( + "encoding/json" + "fmt" + + "github.com/appscode/kutil" + "github.com/golang/glog" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" +) + +func CreateOrPatchNode(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Node) *core.Node) (*core.Node, kutil.VerbType, error) { + cur, err := c.CoreV1().Nodes().Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + glog.V(3).Infof("Creating Node %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().Nodes().Create(transform(&core.Node{ + TypeMeta: metav1.TypeMeta{ + Kind: "Node", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + })) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchNode(c, cur, transform) +} + +func PatchNode(c kubernetes.Interface, cur *core.Node, transform func(*core.Node) *core.Node) (*core.Node, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(transform(cur.DeepCopy())) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.Node{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + glog.V(3).Infof("Patching Node %s with %s", cur.Name, string(patch)) + out, err := c.CoreV1().Nodes().Patch(cur.Name, types.StrategicMergePatchType, patch) + return out, kutil.VerbPatched, err +} + +func TryUpdateNode(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Node) *core.Node) (result *core.Node, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().Nodes().Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().Nodes().Update(transform(cur.DeepCopy())) + return e2 == nil, nil + } + glog.Errorf("Attempt %d failed to update Node %s due to %v.", attempt, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = fmt.Errorf("failed to update Node %s after %d attempts due to %v", meta.Name, attempt, err) + } + return +} + +// NodeReady returns whether a node is ready. +func NodeReady(node core.Node) bool { + for _, cond := range node.Status.Conditions { + if cond.Type != core.NodeReady { + continue + } + return cond.Status == core.ConditionTrue + } + return false +} + +// IsMaster returns whether a node is a master. +func IsMaster(node core.Node) bool { + _, ok17 := node.Labels["node-role.kubernetes.io/master"] + role16, ok16 := node.Labels["kubernetes.io/role"] + return ok17 || (ok16 && role16 == "master") +} diff --git a/vendor/github.com/appscode/kutil/core/v1/pod.go b/vendor/github.com/appscode/kutil/core/v1/pod.go new file mode 100644 index 000000000..3b1054fe2 --- /dev/null +++ b/vendor/github.com/appscode/kutil/core/v1/pod.go @@ -0,0 +1,163 @@ +package v1 + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/appscode/kutil" + "github.com/golang/glog" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" +) + +func CreateOrPatchPod(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Pod) *core.Pod) (*core.Pod, kutil.VerbType, error) { + cur, err := c.CoreV1().Pods(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + glog.V(3).Infof("Creating Pod %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().Pods(meta.Namespace).Create(transform(&core.Pod{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pod", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + })) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchPod(c, cur, transform) +} + +func PatchPod(c kubernetes.Interface, cur *core.Pod, transform func(*core.Pod) *core.Pod) (*core.Pod, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(transform(cur.DeepCopy())) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.Pod{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + glog.V(3).Infof("Patching Pod %s/%s with %s", cur.Namespace, cur.Name, string(patch)) + out, err := c.CoreV1().Pods(cur.Namespace).Patch(cur.Name, types.StrategicMergePatchType, patch) + return out, kutil.VerbPatched, err +} + +func TryUpdatePod(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Pod) *core.Pod) (result *core.Pod, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().Pods(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().Pods(cur.Namespace).Update(transform(cur.DeepCopy())) + return e2 == nil, nil + } + glog.Errorf("Attempt %d failed to update Pod %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = fmt.Errorf("failed to update Pod %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} + +// ref: https://github.com/coreos/prometheus-operator/blob/c79166fcff3dae7bb8bc1e6bddc81837c2d97c04/pkg/k8sutil/k8sutil.go#L64 +// PodRunningAndReady returns whether a pod is running and each container has +// passed it's ready state. +func PodRunningAndReady(pod core.Pod) (bool, error) { + switch pod.Status.Phase { + case core.PodFailed, core.PodSucceeded: + return false, errors.New("pod completed") + case core.PodRunning: + for _, cond := range pod.Status.Conditions { + if cond.Type != core.PodReady { + continue + } + return cond.Status == core.ConditionTrue, nil + } + return false, errors.New("pod ready condition not found") + } + return false, nil +} + +func RestartPods(kubeClient kubernetes.Interface, namespace string, selector *metav1.LabelSelector) error { + r, err := metav1.LabelSelectorAsSelector(selector) + if err != nil { + return err + } + return kubeClient.CoreV1().Pods(namespace).DeleteCollection(&metav1.DeleteOptions{}, metav1.ListOptions{ + LabelSelector: r.String(), + }) +} + +func WaitUntilPodRunning(kubeClient kubernetes.Interface, meta metav1.ObjectMeta) error { + return wait.PollImmediate(kutil.RetryInterval, kutil.ReadinessTimeout, func() (bool, error) { + if pod, err := kubeClient.CoreV1().Pods(meta.Namespace).Get(meta.Name, metav1.GetOptions{}); err == nil { + runningAndReady, _ := PodRunningAndReady(*pod) + return runningAndReady, nil + } + return false, nil + }) +} + +func WaitUntilPodRunningBySelector(kubeClient kubernetes.Interface, namespace string, selector *metav1.LabelSelector, count int) error { + r, err := metav1.LabelSelectorAsSelector(selector) + if err != nil { + return err + } + + return wait.PollImmediate(kutil.RetryInterval, kutil.ReadinessTimeout, func() (bool, error) { + podList, err := kubeClient.CoreV1().Pods(namespace).List(metav1.ListOptions{ + LabelSelector: r.String(), + }) + if err != nil { + return false, nil + } + + if len(podList.Items) != count { + return false, nil + } + + for _, pod := range podList.Items { + runningAndReady, _ := PodRunningAndReady(pod) + if !runningAndReady { + return false, nil + } + } + return true, nil + }) +} + +func WaitUntilPodDeletedBySelector(kubeClient kubernetes.Interface, namespace string, selector *metav1.LabelSelector) error { + r, err := metav1.LabelSelectorAsSelector(selector) + if err != nil { + return err + } + + return wait.PollImmediate(kutil.RetryInterval, kutil.ReadinessTimeout, func() (bool, error) { + podList, err := kubeClient.CoreV1().Pods(namespace).List(metav1.ListOptions{ + LabelSelector: r.String(), + }) + if err != nil { + return false, nil + } + return len(podList.Items) == 0, nil + }) +} diff --git a/vendor/github.com/appscode/kutil/core/v1/pv.go b/vendor/github.com/appscode/kutil/core/v1/pv.go new file mode 100644 index 000000000..192e124ef --- /dev/null +++ b/vendor/github.com/appscode/kutil/core/v1/pv.go @@ -0,0 +1,78 @@ +package v1 + +import ( + "encoding/json" + "fmt" + + "github.com/appscode/kutil" + "github.com/golang/glog" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" +) + +func CreateOrPatchPV(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.PersistentVolume) *core.PersistentVolume) (*core.PersistentVolume, kutil.VerbType, error) { + cur, err := c.CoreV1().PersistentVolumes().Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + glog.V(3).Infof("Creating PersistentVolume %s.", meta.Name) + out, err := c.CoreV1().PersistentVolumes().Create(transform(&core.PersistentVolume{ + TypeMeta: metav1.TypeMeta{ + Kind: "PersistentVolume", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + })) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchPV(c, cur, transform) +} + +func PatchPV(c kubernetes.Interface, cur *core.PersistentVolume, transform func(*core.PersistentVolume) *core.PersistentVolume) (*core.PersistentVolume, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(transform(cur.DeepCopy())) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.PersistentVolume{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + glog.V(3).Infof("Patching PersistentVolume %s with %s.", cur.Name, string(patch)) + out, err := c.CoreV1().PersistentVolumes().Patch(cur.Name, types.StrategicMergePatchType, patch) + return out, kutil.VerbPatched, err +} + +func TryUpdatePV(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.PersistentVolume) *core.PersistentVolume) (result *core.PersistentVolume, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().PersistentVolumes().Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().PersistentVolumes().Update(transform(cur.DeepCopy())) + return e2 == nil, nil + } + glog.Errorf("Attempt %d failed to update PersistentVolume %s due to %v.", attempt, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = fmt.Errorf("failed to update PersistentVolume %s after %d attempts due to %v", meta.Name, attempt, err) + } + return +} diff --git a/vendor/github.com/appscode/kutil/core/v1/pvc.go b/vendor/github.com/appscode/kutil/core/v1/pvc.go new file mode 100644 index 000000000..37f8c4be2 --- /dev/null +++ b/vendor/github.com/appscode/kutil/core/v1/pvc.go @@ -0,0 +1,78 @@ +package v1 + +import ( + "encoding/json" + "fmt" + + "github.com/appscode/kutil" + "github.com/golang/glog" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" +) + +func CreateOrPatchPVC(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.PersistentVolumeClaim) *core.PersistentVolumeClaim) (*core.PersistentVolumeClaim, kutil.VerbType, error) { + cur, err := c.CoreV1().PersistentVolumeClaims(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + glog.V(3).Infof("Creating PersistentVolumeClaim %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().PersistentVolumeClaims(meta.Namespace).Create(transform(&core.PersistentVolumeClaim{ + TypeMeta: metav1.TypeMeta{ + Kind: "PersistentVolumeClaim", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + })) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchPVC(c, cur, transform) +} + +func PatchPVC(c kubernetes.Interface, cur *core.PersistentVolumeClaim, transform func(*core.PersistentVolumeClaim) *core.PersistentVolumeClaim) (*core.PersistentVolumeClaim, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(transform(cur.DeepCopy())) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.PersistentVolumeClaim{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + glog.V(3).Infof("Patching PersistentVolumeClaim %s/%s with %s.", cur.Namespace, cur.Name, string(patch)) + out, err := c.CoreV1().PersistentVolumeClaims(cur.Namespace).Patch(cur.Name, types.StrategicMergePatchType, patch) + return out, kutil.VerbPatched, err +} + +func TryUpdatePVC(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.PersistentVolumeClaim) *core.PersistentVolumeClaim) (result *core.PersistentVolumeClaim, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().PersistentVolumeClaims(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().PersistentVolumeClaims(cur.Namespace).Update(transform(cur.DeepCopy())) + return e2 == nil, nil + } + glog.Errorf("Attempt %d failed to update PersistentVolumeClaim %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = fmt.Errorf("failed to update PersistentVolumeClaim %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} diff --git a/vendor/github.com/appscode/kutil/core/v1/rc.go b/vendor/github.com/appscode/kutil/core/v1/rc.go new file mode 100644 index 000000000..07de7170e --- /dev/null +++ b/vendor/github.com/appscode/kutil/core/v1/rc.go @@ -0,0 +1,89 @@ +package v1 + +import ( + "encoding/json" + "fmt" + + . "github.com/appscode/go/types" + "github.com/appscode/kutil" + "github.com/golang/glog" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" +) + +func CreateOrPatchRC(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.ReplicationController) *core.ReplicationController) (*core.ReplicationController, kutil.VerbType, error) { + cur, err := c.CoreV1().ReplicationControllers(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + glog.V(3).Infof("Creating ReplicationController %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().ReplicationControllers(meta.Namespace).Create(transform(&core.ReplicationController{ + TypeMeta: metav1.TypeMeta{ + Kind: "ReplicationController", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + })) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchRC(c, cur, transform) +} + +func PatchRC(c kubernetes.Interface, cur *core.ReplicationController, transform func(*core.ReplicationController) *core.ReplicationController) (*core.ReplicationController, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(transform(cur.DeepCopy())) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.ReplicationController{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + glog.V(3).Infof("Patching ReplicationController %s/%s with %s.", cur.Namespace, cur.Name, string(patch)) + out, err := c.CoreV1().ReplicationControllers(cur.Namespace).Patch(cur.Name, types.StrategicMergePatchType, patch) + return out, kutil.VerbPatched, err +} + +func TryUpdateRC(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.ReplicationController) *core.ReplicationController) (result *core.ReplicationController, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().ReplicationControllers(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().ReplicationControllers(cur.Namespace).Update(transform(cur.DeepCopy())) + return e2 == nil, nil + } + glog.Errorf("Attempt %d failed to update ReplicationController %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = fmt.Errorf("failed to update ReplicationController %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} + +func WaitUntilRCReady(c kubernetes.Interface, meta metav1.ObjectMeta) error { + return wait.PollImmediate(kutil.RetryInterval, kutil.ReadinessTimeout, func() (bool, error) { + if obj, err := c.CoreV1().ReplicationControllers(meta.Namespace).Get(meta.Name, metav1.GetOptions{}); err == nil { + return Int32(obj.Spec.Replicas) == obj.Status.ReadyReplicas, nil + } + + return false, nil + }) +} diff --git a/vendor/github.com/appscode/kutil/core/v1/secret.go b/vendor/github.com/appscode/kutil/core/v1/secret.go new file mode 100644 index 000000000..ffeee9155 --- /dev/null +++ b/vendor/github.com/appscode/kutil/core/v1/secret.go @@ -0,0 +1,87 @@ +package v1 + +import ( + "encoding/json" + "fmt" + + "github.com/appscode/kutil" + "github.com/golang/glog" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" +) + +func CreateOrPatchSecret(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Secret) *core.Secret) (*core.Secret, kutil.VerbType, error) { + cur, err := c.CoreV1().Secrets(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + glog.V(3).Infof("Creating Secret %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().Secrets(meta.Namespace).Create(transform(&core.Secret{ + TypeMeta: metav1.TypeMeta{ + Kind: "Secret", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + })) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchSecret(c, cur, transform) +} + +func PatchSecret(c kubernetes.Interface, cur *core.Secret, transform func(*core.Secret) *core.Secret) (*core.Secret, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(transform(cur.DeepCopy())) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.Secret{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + glog.V(3).Infof("Patching Secret %s/%s", cur.Namespace, cur.Name) + out, err := c.CoreV1().Secrets(cur.Namespace).Patch(cur.Name, types.StrategicMergePatchType, patch) + return out, kutil.VerbPatched, err +} + +func TryUpdateSecret(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Secret) *core.Secret) (result *core.Secret, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().Secrets(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().Secrets(cur.Namespace).Update(transform(cur.DeepCopy())) + return e2 == nil, nil + } + glog.Errorf("Attempt %d failed to update Secret %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = fmt.Errorf("failed to update Secret %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} + +func ObfuscateSecret(in core.Secret) *core.Secret { + data := make(map[string][]byte) + for k := range in.Data { + data[k] = []byte("-") + } + in.Data = data + return &in +} diff --git a/vendor/github.com/appscode/kutil/core/v1/service.go b/vendor/github.com/appscode/kutil/core/v1/service.go new file mode 100644 index 000000000..6a7cdfefe --- /dev/null +++ b/vendor/github.com/appscode/kutil/core/v1/service.go @@ -0,0 +1,107 @@ +package v1 + +import ( + "encoding/json" + "fmt" + + "github.com/appscode/kutil" + "github.com/golang/glog" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" +) + +func CreateOrPatchService(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Service) *core.Service) (*core.Service, kutil.VerbType, error) { + cur, err := c.CoreV1().Services(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + glog.V(3).Infof("Creating Service %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().Services(meta.Namespace).Create(transform(&core.Service{ + TypeMeta: metav1.TypeMeta{ + Kind: "Service", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + })) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchService(c, cur, transform) +} + +func PatchService(c kubernetes.Interface, cur *core.Service, transform func(*core.Service) *core.Service) (*core.Service, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(transform(cur.DeepCopy())) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.Service{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + glog.V(3).Infof("Patching Service %s/%s with %s.", cur.Namespace, cur.Name, string(patch)) + out, err := c.CoreV1().Services(cur.Namespace).Patch(cur.Name, types.StrategicMergePatchType, patch) + return out, kutil.VerbPatched, err +} + +func TryUpdateService(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.Service) *core.Service) (result *core.Service, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().Services(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().Services(cur.Namespace).Update(transform(cur.DeepCopy())) + return e2 == nil, nil + } + glog.Errorf("Attempt %d failed to update Service %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = fmt.Errorf("failed to update Service %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} + +func MergeServicePorts(cur, desired []core.ServicePort) []core.ServicePort { + if len(cur) == 0 { + return desired + } + + // ports + curPorts := make(map[int32]core.ServicePort) + for _, p := range cur { + curPorts[p.Port] = p + } + for i, dp := range desired { + cp, ok := curPorts[dp.Port] + + // svc port not found + if !ok { + continue + } + + if dp.NodePort == 0 { + dp.NodePort = cp.NodePort // avoid reassigning port + } + if dp.Protocol == "" { + dp.Protocol = cp.Protocol + } + desired[i] = dp + } + return desired +} diff --git a/vendor/github.com/appscode/kutil/core/v1/serviceaccount.go b/vendor/github.com/appscode/kutil/core/v1/serviceaccount.go new file mode 100644 index 000000000..1a64b94d1 --- /dev/null +++ b/vendor/github.com/appscode/kutil/core/v1/serviceaccount.go @@ -0,0 +1,78 @@ +package v1 + +import ( + "encoding/json" + "fmt" + + "github.com/appscode/kutil" + "github.com/golang/glog" + core "k8s.io/api/core/v1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" +) + +func CreateOrPatchServiceAccount(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.ServiceAccount) *core.ServiceAccount) (*core.ServiceAccount, kutil.VerbType, error) { + cur, err := c.CoreV1().ServiceAccounts(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + glog.V(3).Infof("Creating ServiceAccount %s/%s.", meta.Namespace, meta.Name) + out, err := c.CoreV1().ServiceAccounts(meta.Namespace).Create(transform(&core.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + Kind: "ServiceAccount", + APIVersion: core.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + })) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchServiceAccount(c, cur, transform) +} + +func PatchServiceAccount(c kubernetes.Interface, cur *core.ServiceAccount, transform func(*core.ServiceAccount) *core.ServiceAccount) (*core.ServiceAccount, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(transform(cur.DeepCopy())) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, core.ServiceAccount{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + glog.V(3).Infof("Patching ServiceAccount %s/%s with %s", cur.Namespace, cur.Name, string(patch)) + out, err := c.CoreV1().ServiceAccounts(cur.Namespace).Patch(cur.Name, types.StrategicMergePatchType, patch) + return out, kutil.VerbPatched, err +} + +func TryUpdateServiceAccount(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*core.ServiceAccount) *core.ServiceAccount) (result *core.ServiceAccount, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.CoreV1().ServiceAccounts(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.CoreV1().ServiceAccounts(cur.Namespace).Update(transform(cur.DeepCopy())) + return e2 == nil, nil + } + glog.Errorf("Attempt %d failed to update ServiceAccount %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = fmt.Errorf("failed to update ServiceAccount %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} diff --git a/vendor/github.com/appscode/kutil/rbac/v1beta1/clusterrole.go b/vendor/github.com/appscode/kutil/rbac/v1beta1/clusterrole.go new file mode 100644 index 000000000..93c136d9f --- /dev/null +++ b/vendor/github.com/appscode/kutil/rbac/v1beta1/clusterrole.go @@ -0,0 +1,78 @@ +package v1beta1 + +import ( + "encoding/json" + "fmt" + + "github.com/appscode/kutil" + "github.com/golang/glog" + rbac "k8s.io/api/rbac/v1beta1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" +) + +func CreateOrPatchClusterRole(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*rbac.ClusterRole) *rbac.ClusterRole) (*rbac.ClusterRole, kutil.VerbType, error) { + cur, err := c.RbacV1beta1().ClusterRoles().Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + glog.V(3).Infof("Creating ClusterRole %s.", meta.Name) + out, err := c.RbacV1beta1().ClusterRoles().Create(transform(&rbac.ClusterRole{ + TypeMeta: metav1.TypeMeta{ + Kind: "ClusterRole", + APIVersion: rbac.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + })) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchClusterRole(c, cur, transform) +} + +func PatchClusterRole(c kubernetes.Interface, cur *rbac.ClusterRole, transform func(*rbac.ClusterRole) *rbac.ClusterRole) (*rbac.ClusterRole, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(transform(cur.DeepCopy())) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, rbac.ClusterRole{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + glog.V(3).Infof("Patching ClusterRole %s with %s.", cur.Name, string(patch)) + out, err := c.RbacV1beta1().ClusterRoles().Patch(cur.Name, types.StrategicMergePatchType, patch) + return out, kutil.VerbPatched, err +} + +func TryUpdateClusterRole(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*rbac.ClusterRole) *rbac.ClusterRole) (result *rbac.ClusterRole, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.RbacV1beta1().ClusterRoles().Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.RbacV1beta1().ClusterRoles().Update(transform(cur.DeepCopy())) + return e2 == nil, nil + } + glog.Errorf("Attempt %d failed to update ClusterRole %s due to %v.", attempt, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = fmt.Errorf("failed to update ClusterRole %s after %d attempts due to %v", meta.Name, attempt, err) + } + return +} diff --git a/vendor/github.com/appscode/kutil/rbac/v1beta1/clusterrolebinding.go b/vendor/github.com/appscode/kutil/rbac/v1beta1/clusterrolebinding.go new file mode 100644 index 000000000..87735ff23 --- /dev/null +++ b/vendor/github.com/appscode/kutil/rbac/v1beta1/clusterrolebinding.go @@ -0,0 +1,78 @@ +package v1beta1 + +import ( + "encoding/json" + "fmt" + + "github.com/appscode/kutil" + "github.com/golang/glog" + rbac "k8s.io/api/rbac/v1beta1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" +) + +func CreateOrPatchClusterRoleBinding(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*rbac.ClusterRoleBinding) *rbac.ClusterRoleBinding) (*rbac.ClusterRoleBinding, kutil.VerbType, error) { + cur, err := c.RbacV1beta1().ClusterRoleBindings().Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + glog.V(3).Infof("Creating ClusterRoleBinding %s.", meta.Name) + out, err := c.RbacV1beta1().ClusterRoleBindings().Create(transform(&rbac.ClusterRoleBinding{ + TypeMeta: metav1.TypeMeta{ + Kind: "ClusterRoleBinding", + APIVersion: rbac.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + })) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchClusterRoleBinding(c, cur, transform) +} + +func PatchClusterRoleBinding(c kubernetes.Interface, cur *rbac.ClusterRoleBinding, transform func(*rbac.ClusterRoleBinding) *rbac.ClusterRoleBinding) (*rbac.ClusterRoleBinding, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(transform(cur.DeepCopy())) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, rbac.ClusterRoleBinding{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + glog.V(3).Infof("Patching ClusterRoleBinding %s with %s.", cur.Name, string(patch)) + out, err := c.RbacV1beta1().ClusterRoleBindings().Patch(cur.Name, types.StrategicMergePatchType, patch) + return out, kutil.VerbPatched, err +} + +func TryUpdateClusterRoleBinding(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*rbac.ClusterRoleBinding) *rbac.ClusterRoleBinding) (result *rbac.ClusterRoleBinding, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.RbacV1beta1().ClusterRoleBindings().Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.RbacV1beta1().ClusterRoleBindings().Update(transform(cur.DeepCopy())) + return e2 == nil, nil + } + glog.Errorf("Attempt %d failed to update ClusterRoleBinding %s due to %v.", attempt, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = fmt.Errorf("failed to update Role %s after %d attempts due to %v", meta.Name, attempt, err) + } + return +} diff --git a/vendor/github.com/appscode/kutil/rbac/v1beta1/kubernetes.go b/vendor/github.com/appscode/kutil/rbac/v1beta1/kubernetes.go new file mode 100644 index 000000000..cc74aa73c --- /dev/null +++ b/vendor/github.com/appscode/kutil/rbac/v1beta1/kubernetes.go @@ -0,0 +1,41 @@ +package v1beta1 + +import ( + "errors" + + "github.com/appscode/kutil/meta" + rbac "k8s.io/api/rbac/v1beta1" + "k8s.io/apimachinery/pkg/conversion" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func GetGroupVersionKind(v interface{}) schema.GroupVersionKind { + return rbac.SchemeGroupVersion.WithKind(meta.GetKind(v)) +} + +func AssignTypeKind(v interface{}) error { + _, err := conversion.EnforcePtr(v) + if err != nil { + return err + } + + switch u := v.(type) { + case *rbac.Role: + u.APIVersion = rbac.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *rbac.RoleBinding: + u.APIVersion = rbac.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *rbac.ClusterRole: + u.APIVersion = rbac.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + case *rbac.ClusterRoleBinding: + u.APIVersion = rbac.SchemeGroupVersion.String() + u.Kind = meta.GetKind(v) + return nil + } + return errors.New("unknown api object type") +} diff --git a/vendor/github.com/appscode/kutil/rbac/v1beta1/role.go b/vendor/github.com/appscode/kutil/rbac/v1beta1/role.go new file mode 100644 index 000000000..692485698 --- /dev/null +++ b/vendor/github.com/appscode/kutil/rbac/v1beta1/role.go @@ -0,0 +1,78 @@ +package v1beta1 + +import ( + "encoding/json" + "fmt" + + "github.com/appscode/kutil" + "github.com/golang/glog" + rbac "k8s.io/api/rbac/v1beta1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" +) + +func CreateOrPatchRole(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*rbac.Role) *rbac.Role) (*rbac.Role, kutil.VerbType, error) { + cur, err := c.RbacV1beta1().Roles(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + glog.V(3).Infof("Creating Role %s/%s.", meta.Namespace, meta.Name) + out, err := c.RbacV1beta1().Roles(meta.Namespace).Create(transform(&rbac.Role{ + TypeMeta: metav1.TypeMeta{ + Kind: "Role", + APIVersion: rbac.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + })) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchRole(c, cur, transform) +} + +func PatchRole(c kubernetes.Interface, cur *rbac.Role, transform func(*rbac.Role) *rbac.Role) (*rbac.Role, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(transform(cur.DeepCopy())) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, rbac.Role{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + glog.V(3).Infof("Patching Role %s/%s with %s.", cur.Namespace, cur.Name, string(patch)) + out, err := c.RbacV1beta1().Roles(cur.Namespace).Patch(cur.Name, types.StrategicMergePatchType, patch) + return out, kutil.VerbPatched, err +} + +func TryUpdateRole(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*rbac.Role) *rbac.Role) (result *rbac.Role, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.RbacV1beta1().Roles(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.RbacV1beta1().Roles(cur.Namespace).Update(transform(cur.DeepCopy())) + return e2 == nil, nil + } + glog.Errorf("Attempt %d failed to update Role %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = fmt.Errorf("failed to update Role %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} diff --git a/vendor/github.com/appscode/kutil/rbac/v1beta1/rolebinding.go b/vendor/github.com/appscode/kutil/rbac/v1beta1/rolebinding.go new file mode 100644 index 000000000..6589237d6 --- /dev/null +++ b/vendor/github.com/appscode/kutil/rbac/v1beta1/rolebinding.go @@ -0,0 +1,78 @@ +package v1beta1 + +import ( + "encoding/json" + "fmt" + + "github.com/appscode/kutil" + "github.com/golang/glog" + rbac "k8s.io/api/rbac/v1beta1" + kerr "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" +) + +func CreateOrPatchRoleBinding(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*rbac.RoleBinding) *rbac.RoleBinding) (*rbac.RoleBinding, kutil.VerbType, error) { + cur, err := c.RbacV1beta1().RoleBindings(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(err) { + glog.V(3).Infof("Creating RoleBinding %s/%s.", meta.Namespace, meta.Name) + out, err := c.RbacV1beta1().RoleBindings(meta.Namespace).Create(transform(&rbac.RoleBinding{ + TypeMeta: metav1.TypeMeta{ + Kind: "RoleBinding", + APIVersion: rbac.SchemeGroupVersion.String(), + }, + ObjectMeta: meta, + })) + return out, kutil.VerbCreated, err + } else if err != nil { + return nil, kutil.VerbUnchanged, err + } + return PatchRoleBinding(c, cur, transform) +} + +func PatchRoleBinding(c kubernetes.Interface, cur *rbac.RoleBinding, transform func(*rbac.RoleBinding) *rbac.RoleBinding) (*rbac.RoleBinding, kutil.VerbType, error) { + curJson, err := json.Marshal(cur) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + modJson, err := json.Marshal(transform(cur.DeepCopy())) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + + patch, err := strategicpatch.CreateTwoWayMergePatch(curJson, modJson, rbac.RoleBinding{}) + if err != nil { + return nil, kutil.VerbUnchanged, err + } + if len(patch) == 0 || string(patch) == "{}" { + return cur, kutil.VerbUnchanged, nil + } + glog.V(3).Infof("Patching RoleBinding %s/%s with %s.", cur.Namespace, cur.Name, string(patch)) + out, err := c.RbacV1beta1().RoleBindings(cur.Namespace).Patch(cur.Name, types.StrategicMergePatchType, patch) + return out, kutil.VerbPatched, err +} + +func TryUpdateRoleBinding(c kubernetes.Interface, meta metav1.ObjectMeta, transform func(*rbac.RoleBinding) *rbac.RoleBinding) (result *rbac.RoleBinding, err error) { + attempt := 0 + err = wait.PollImmediate(kutil.RetryInterval, kutil.RetryTimeout, func() (bool, error) { + attempt++ + cur, e2 := c.RbacV1beta1().RoleBindings(meta.Namespace).Get(meta.Name, metav1.GetOptions{}) + if kerr.IsNotFound(e2) { + return false, e2 + } else if e2 == nil { + result, e2 = c.RbacV1beta1().RoleBindings(cur.Namespace).Update(transform(cur.DeepCopy())) + return e2 == nil, nil + } + glog.Errorf("Attempt %d failed to update RoleBinding %s/%s due to %v.", attempt, cur.Namespace, cur.Name, e2) + return false, nil + }) + + if err != nil { + err = fmt.Errorf("failed to update Role %s/%s after %d attempts due to %v", meta.Namespace, meta.Name, attempt, err) + } + return +} diff --git a/vendor/github.com/appscode/mergo/LICENSE b/vendor/github.com/appscode/mergo/LICENSE new file mode 100644 index 000000000..686680298 --- /dev/null +++ b/vendor/github.com/appscode/mergo/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2013 Dario Castañé. All rights reserved. +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/appscode/mergo/doc.go b/vendor/github.com/appscode/mergo/doc.go new file mode 100644 index 000000000..6e9aa7baf --- /dev/null +++ b/vendor/github.com/appscode/mergo/doc.go @@ -0,0 +1,44 @@ +// Copyright 2013 Dario Castañé. All rights reserved. +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package mergo merges same-type structs and maps by setting default values in zero-value fields. + +Mergo won't merge unexported (private) fields but will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection). + +Usage + +From my own work-in-progress project: + + type networkConfig struct { + Protocol string + Address string + ServerType string `json: "server_type"` + Port uint16 + } + + type FssnConfig struct { + Network networkConfig + } + + var fssnDefault = FssnConfig { + networkConfig { + "tcp", + "127.0.0.1", + "http", + 31560, + }, + } + + // Inside a function [...] + + if err := mergo.Merge(&config, fssnDefault); err != nil { + log.Fatal(err) + } + + // More code [...] + +*/ +package mergo diff --git a/vendor/github.com/appscode/mergo/map.go b/vendor/github.com/appscode/mergo/map.go new file mode 100644 index 000000000..8e8c4ba8e --- /dev/null +++ b/vendor/github.com/appscode/mergo/map.go @@ -0,0 +1,156 @@ +// Copyright 2014 Dario Castañé. All rights reserved. +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Based on src/pkg/reflect/deepequal.go from official +// golang's stdlib. + +package mergo + +import ( + "fmt" + "reflect" + "unicode" + "unicode/utf8" +) + +func changeInitialCase(s string, mapper func(rune) rune) string { + if s == "" { + return s + } + r, n := utf8.DecodeRuneInString(s) + return string(mapper(r)) + s[n:] +} + +func isExported(field reflect.StructField) bool { + r, _ := utf8.DecodeRuneInString(field.Name) + return r >= 'A' && r <= 'Z' +} + +// Traverses recursively both values, assigning src's fields values to dst. +// The map argument tracks comparisons that have already been seen, which allows +// short circuiting on recursive types. +func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) { + if dst.CanAddr() { + addr := dst.UnsafeAddr() + h := 17 * addr + seen := visited[h] + typ := dst.Type() + for p := seen; p != nil; p = p.next { + if p.ptr == addr && p.typ == typ { + return nil + } + } + // Remember, remember... + visited[h] = &visit{addr, typ, seen} + } + zeroValue := reflect.Value{} + switch dst.Kind() { + case reflect.Map: + dstMap := dst.Interface().(map[string]interface{}) + for i, n := 0, src.NumField(); i < n; i++ { + srcType := src.Type() + field := srcType.Field(i) + if !isExported(field) { + continue + } + fieldName := field.Name + fieldName = changeInitialCase(fieldName, unicode.ToLower) + if v, ok := dstMap[fieldName]; !ok || (isEmptyValue(reflect.ValueOf(v)) || overwrite) { + dstMap[fieldName] = src.Field(i).Interface() + } + } + case reflect.Struct: + srcMap := src.Interface().(map[string]interface{}) + for key := range srcMap { + srcValue := srcMap[key] + fieldName := changeInitialCase(key, unicode.ToUpper) + dstElement := dst.FieldByName(fieldName) + if dstElement == zeroValue { + // We discard it because the field doesn't exist. + continue + } + srcElement := reflect.ValueOf(srcValue) + dstKind := dstElement.Kind() + srcKind := srcElement.Kind() + if srcKind == reflect.Ptr && dstKind != reflect.Ptr { + srcElement = srcElement.Elem() + srcKind = reflect.TypeOf(srcElement.Interface()).Kind() + } else if dstKind == reflect.Ptr { + // Can this work? I guess it can't. + if srcKind != reflect.Ptr && srcElement.CanAddr() { + srcPtr := srcElement.Addr() + srcElement = reflect.ValueOf(srcPtr) + srcKind = reflect.Ptr + } + } + if !srcElement.IsValid() { + continue + } + if srcKind == dstKind { + if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil { + return + } + } else { + if srcKind == reflect.Map { + if err = deepMap(dstElement, srcElement, visited, depth+1, overwrite); err != nil { + return + } + } else { + return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind) + } + } + } + } + return +} + +// Map sets fields' values in dst from src. +// src can be a map with string keys or a struct. dst must be the opposite: +// if src is a map, dst must be a valid pointer to struct. If src is a struct, +// dst must be map[string]interface{}. +// It won't merge unexported (private) fields and will do recursively +// any exported field. +// If dst is a map, keys will be src fields' names in lower camel case. +// Missing key in src that doesn't match a field in dst will be skipped. This +// doesn't apply if dst is a map. +// This is separated method from Merge because it is cleaner and it keeps sane +// semantics: merging equal types, mapping different (restricted) types. +func Map(dst, src interface{}) error { + return _map(dst, src, false) +} + +// MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overriden by +// non-empty src attribute values. +func MapWithOverwrite(dst, src interface{}) error { + return _map(dst, src, true) +} + +func _map(dst, src interface{}, overwrite bool) error { + var ( + vDst, vSrc reflect.Value + err error + ) + if vDst, vSrc, err = resolveValues(dst, src); err != nil { + return err + } + // To be friction-less, we redirect equal-type arguments + // to deepMerge. Only because arguments can be anything. + if vSrc.Kind() == vDst.Kind() { + return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite) + } + switch vSrc.Kind() { + case reflect.Struct: + if vDst.Kind() != reflect.Map { + return ErrExpectedMapAsDestination + } + case reflect.Map: + if vDst.Kind() != reflect.Struct { + return ErrExpectedStructAsDestination + } + default: + return ErrNotSupported + } + return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite) +} diff --git a/vendor/github.com/appscode/mergo/merge.go b/vendor/github.com/appscode/mergo/merge.go new file mode 100644 index 000000000..513774f4c --- /dev/null +++ b/vendor/github.com/appscode/mergo/merge.go @@ -0,0 +1,123 @@ +// Copyright 2013 Dario Castañé. All rights reserved. +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Based on src/pkg/reflect/deepequal.go from official +// golang's stdlib. + +package mergo + +import ( + "reflect" +) + +// Traverses recursively both values, assigning src's fields values to dst. +// The map argument tracks comparisons that have already been seen, which allows +// short circuiting on recursive types. +func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) { + if !src.IsValid() { + return + } + if dst.CanAddr() { + addr := dst.UnsafeAddr() + h := 17 * addr + seen := visited[h] + typ := dst.Type() + for p := seen; p != nil; p = p.next { + if p.ptr == addr && p.typ == typ { + return nil + } + } + // Remember, remember... + visited[h] = &visit{addr, typ, seen} + } + switch dst.Kind() { + case reflect.Struct: + for i, n := 0, dst.NumField(); i < n; i++ { + if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, overwrite); err != nil { + return + } + } + case reflect.Map: + for _, key := range src.MapKeys() { + srcElement := src.MapIndex(key) + if !srcElement.IsValid() { + continue + } + dstElement := dst.MapIndex(key) + switch srcElement.Kind() { + case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice: + if srcElement.IsNil() { + continue + } + fallthrough + default: + if !srcElement.CanInterface() { + continue + } + switch reflect.TypeOf(srcElement.Interface()).Kind() { + case reflect.Struct: + fallthrough + case reflect.Ptr: + fallthrough + case reflect.Map: + if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil { + return + } + } + } + if !isEmptyValue(srcElement) && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) { + if dst.IsNil() { + dst.Set(reflect.MakeMap(dst.Type())) + } + dst.SetMapIndex(key, srcElement) + } + } + case reflect.Ptr: + fallthrough + case reflect.Interface: + if src.IsNil() { + break + } else if dst.IsNil() || overwrite { + if dst.CanSet() && (overwrite || isEmptyValue(dst)) { + dst.Set(src) + } + } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, overwrite); err != nil { + return + } + default: + if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) { + dst.Set(src) + } + } + return +} + +// Merge will fill any empty for value type attributes on the dst struct using corresponding +// src attributes if they themselves are not empty. dst and src must be valid same-type structs +// and dst must be a pointer to struct. +// It won't merge unexported (private) fields and will do recursively any exported field. +func Merge(dst, src interface{}) error { + return merge(dst, src, false) +} + +// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by +// non-empty src attribute values. +func MergeWithOverwrite(dst, src interface{}) error { + return merge(dst, src, true) +} + +func merge(dst, src interface{}, overwrite bool) error { + var ( + vDst, vSrc reflect.Value + err error + ) + if vDst, vSrc, err = resolveValues(dst, src); err != nil { + return err + } + if vDst.Type() != vSrc.Type() { + return ErrDifferentArgumentsTypes + } + return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite) +} diff --git a/vendor/github.com/appscode/mergo/mergo.go b/vendor/github.com/appscode/mergo/mergo.go new file mode 100644 index 000000000..f8a0991ec --- /dev/null +++ b/vendor/github.com/appscode/mergo/mergo.go @@ -0,0 +1,90 @@ +// Copyright 2013 Dario Castañé. All rights reserved. +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Based on src/pkg/reflect/deepequal.go from official +// golang's stdlib. + +package mergo + +import ( + "errors" + "reflect" +) + +// Errors reported by Mergo when it finds invalid arguments. +var ( + ErrNilArguments = errors.New("src and dst must not be nil") + ErrDifferentArgumentsTypes = errors.New("src and dst must be of same type") + ErrNotSupported = errors.New("only structs and maps are supported") + ErrExpectedMapAsDestination = errors.New("dst was expected to be a map") + ErrExpectedStructAsDestination = errors.New("dst was expected to be a struct") +) + +// During deepMerge, must keep track of checks that are +// in progress. The comparison algorithm assumes that all +// checks in progress are true when it reencounters them. +// Visited are stored in a map indexed by 17 * a1 + a2; +type visit struct { + ptr uintptr + typ reflect.Type + next *visit +} + +// From src/pkg/encoding/json. +func isEmptyValue(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + return false +} + +func resolveValues(dst, src interface{}) (vDst, vSrc reflect.Value, err error) { + if dst == nil || src == nil { + err = ErrNilArguments + return + } + vDst = reflect.ValueOf(dst).Elem() + if vDst.Kind() != reflect.Struct && vDst.Kind() != reflect.Map { + err = ErrNotSupported + return + } + vSrc = reflect.ValueOf(src) + // We check if vSrc is a pointer to dereference it. + if vSrc.Kind() == reflect.Ptr { + vSrc = vSrc.Elem() + } + return +} + +// Traverses recursively both values, assigning src's fields values to dst. +// The map argument tracks comparisons that have already been seen, which allows +// short circuiting on recursive types. +func deeper(dst, src reflect.Value, visited map[uintptr]*visit, depth int) (err error) { + if dst.CanAddr() { + addr := dst.UnsafeAddr() + h := 17 * addr + seen := visited[h] + typ := dst.Type() + for p := seen; p != nil; p = p.next { + if p.ptr == addr && p.typ == typ { + return nil + } + } + // Remember, remember... + visited[h] = &visit{addr, typ, seen} + } + return // TODO refactor +}