diff --git a/contrib/completions/bash/oc b/contrib/completions/bash/oc index f6dd0e9b7768..458d59713c66 100644 --- a/contrib/completions/bash/oc +++ b/contrib/completions/bash/oc @@ -8596,6 +8596,7 @@ _oc_describe() must_have_one_noun+=("securitycontextconstraints") must_have_one_noun+=("service") must_have_one_noun+=("serviceaccount") + must_have_one_noun+=("storageclass") must_have_one_noun+=("template") must_have_one_noun+=("user") must_have_one_noun+=("useridentitymapping") @@ -8643,6 +8644,7 @@ _oc_describe() noun_aliases+=("securitycontextconstraintses") noun_aliases+=("serviceaccounts") noun_aliases+=("services") + noun_aliases+=("storageclasses") noun_aliases+=("svc") } diff --git a/contrib/completions/bash/openshift b/contrib/completions/bash/openshift index b9ceb519989c..d54248f38b2e 100644 --- a/contrib/completions/bash/openshift +++ b/contrib/completions/bash/openshift @@ -13203,6 +13203,7 @@ _openshift_cli_describe() must_have_one_noun+=("securitycontextconstraints") must_have_one_noun+=("service") must_have_one_noun+=("serviceaccount") + must_have_one_noun+=("storageclass") must_have_one_noun+=("template") must_have_one_noun+=("user") must_have_one_noun+=("useridentitymapping") @@ -13250,6 +13251,7 @@ _openshift_cli_describe() noun_aliases+=("securitycontextconstraintses") noun_aliases+=("serviceaccounts") noun_aliases+=("services") + noun_aliases+=("storageclasses") noun_aliases+=("svc") } @@ -24415,6 +24417,7 @@ _openshift_kube_describe() must_have_one_noun+=("securitycontextconstraints") must_have_one_noun+=("service") must_have_one_noun+=("serviceaccount") + must_have_one_noun+=("storageclass") noun_aliases=() noun_aliases+=("certificatesigningrequests") noun_aliases+=("cm") @@ -24459,6 +24462,7 @@ _openshift_kube_describe() noun_aliases+=("securitycontextconstraintses") noun_aliases+=("serviceaccounts") noun_aliases+=("services") + noun_aliases+=("storageclasses") noun_aliases+=("svc") } diff --git a/contrib/completions/zsh/oc b/contrib/completions/zsh/oc index e707ccee520a..cdd4c260c33d 100644 --- a/contrib/completions/zsh/oc +++ b/contrib/completions/zsh/oc @@ -8757,6 +8757,7 @@ _oc_describe() must_have_one_noun+=("securitycontextconstraints") must_have_one_noun+=("service") must_have_one_noun+=("serviceaccount") + must_have_one_noun+=("storageclass") must_have_one_noun+=("template") must_have_one_noun+=("user") must_have_one_noun+=("useridentitymapping") @@ -8804,6 +8805,7 @@ _oc_describe() noun_aliases+=("securitycontextconstraintses") noun_aliases+=("serviceaccounts") noun_aliases+=("services") + noun_aliases+=("storageclasses") noun_aliases+=("svc") } diff --git a/contrib/completions/zsh/openshift b/contrib/completions/zsh/openshift index 647fd391b78f..0e51a4dc9cd2 100644 --- a/contrib/completions/zsh/openshift +++ b/contrib/completions/zsh/openshift @@ -13364,6 +13364,7 @@ _openshift_cli_describe() must_have_one_noun+=("securitycontextconstraints") must_have_one_noun+=("service") must_have_one_noun+=("serviceaccount") + must_have_one_noun+=("storageclass") must_have_one_noun+=("template") must_have_one_noun+=("user") must_have_one_noun+=("useridentitymapping") @@ -13411,6 +13412,7 @@ _openshift_cli_describe() noun_aliases+=("securitycontextconstraintses") noun_aliases+=("serviceaccounts") noun_aliases+=("services") + noun_aliases+=("storageclasses") noun_aliases+=("svc") } @@ -24576,6 +24578,7 @@ _openshift_kube_describe() must_have_one_noun+=("securitycontextconstraints") must_have_one_noun+=("service") must_have_one_noun+=("serviceaccount") + must_have_one_noun+=("storageclass") noun_aliases=() noun_aliases+=("certificatesigningrequests") noun_aliases+=("cm") @@ -24620,6 +24623,7 @@ _openshift_kube_describe() noun_aliases+=("securitycontextconstraintses") noun_aliases+=("serviceaccounts") noun_aliases+=("services") + noun_aliases+=("storageclasses") noun_aliases+=("svc") } diff --git a/vendor/k8s.io/kubernetes/pkg/apis/storage/util/helpers.go b/vendor/k8s.io/kubernetes/pkg/apis/storage/util/helpers.go new file mode 100644 index 000000000000..49eada9934ae --- /dev/null +++ b/vendor/k8s.io/kubernetes/pkg/apis/storage/util/helpers.go @@ -0,0 +1,136 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "k8s.io/kubernetes/pkg/api" +) + +// IsDefaultStorageClassAnnotation represents a StorageClass annotation that +// marks a class as the default StorageClass +//TODO: Update IsDefaultStorageClassannotation and remove Beta when no longer used +const IsDefaultStorageClassAnnotation = "storageclass.beta.kubernetes.io/is-default-class" +const BetaIsDefaultStorageClassAnnotation = "storageclass.beta.kubernetes.io/is-default-class" + +// AlphaStorageClassAnnotation represents the previous alpha storage class +// annotation. it's no longer used and held here for posterity. +const AlphaStorageClassAnnotation = "volume.alpha.kubernetes.io/storage-class" + +// BetaStorageClassAnnotation represents the beta/previous StorageClass annotation. +// It's currently still used and will be held for backwards compatibility +const BetaStorageClassAnnotation = "volume.beta.kubernetes.io/storage-class" + +// StorageClassAnnotation represents the storage class associated with a resource. +// It currently matches the Beta value and can change when official is set. +// - in PersistentVolumeClaim it represents required class to match. +// Only PersistentVolumes with the same class (i.e. annotation with the same +// value) can be bound to the claim. In case no such volume exists, the +// controller will provision a new one using StorageClass instance with +// the same name as the annotation value. +// - in PersistentVolume it represents storage class to which the persistent +// volume belongs. +//TODO: Update this to final annotation value as it matches BetaStorageClassAnnotation for now +const StorageClassAnnotation = "volume.beta.kubernetes.io/storage-class" + +// GetVolumeStorageClass returns value of StorageClassAnnotation or empty string in case +// the annotation does not exist. +// TODO: change to PersistentVolume.Spec.Class value when this attribute is +// introduced. +func GetVolumeStorageClass(volume *api.PersistentVolume) string { + if class, found := volume.Annotations[StorageClassAnnotation]; found { + return class + } + + // 'nil' is interpreted as "", i.e. the volume does not belong to any class. + return "" +} + +// GetClaimStorageClass returns name of class that is requested by given claim. +// Request for `nil` class is interpreted as request for class "", +// i.e. for a classless PV. +// TODO: change to PersistentVolumeClaim.Spec.Class value when this +// attribute is introduced. +func GetClaimStorageClass(claim *api.PersistentVolumeClaim) string { + if class, found := claim.Annotations[StorageClassAnnotation]; found { + return class + } + + return "" +} + +// GetStorageClassAnnotation returns the StorageClass value +// if the annotation is set, empty string if not +// TODO: remove Alpha and Beta when no longer used or needed +func GetStorageClassAnnotation(obj api.ObjectMeta) string { + if class, ok := obj.Annotations[StorageClassAnnotation]; ok { + return class + } + if class, ok := obj.Annotations[BetaStorageClassAnnotation]; ok { + return class + } + if class, ok := obj.Annotations[AlphaStorageClassAnnotation]; ok { + return class + } + + return "" +} + +// HasStorageClassAnnotation returns a boolean +// if the annotation is set +// TODO: remove Alpha and Beta when no longer used or needed +func HasStorageClassAnnotation(obj api.ObjectMeta) bool { + if _, found := obj.Annotations[StorageClassAnnotation]; found { + return found + } + if _, found := obj.Annotations[BetaStorageClassAnnotation]; found { + return found + } + if _, found := obj.Annotations[AlphaStorageClassAnnotation]; found { + return found + } + + return false + +} + +// IsDefaultAnnotationText returns a pretty Yes/No String if +// the annotation is set +// TODO: remove Beta when no longer needed +func IsDefaultAnnotationText(obj api.ObjectMeta) string { + if obj.Annotations[IsDefaultStorageClassAnnotation] == "true" { + return "Yes" + } + if obj.Annotations[BetaIsDefaultStorageClassAnnotation] == "true" { + return "Yes" + } + + return "No" +} + +// IsDefaultAnnotation returns a boolean if +// the annotation is set +// TODO: remove Beta when no longer needed +func IsDefaultAnnotation(obj api.ObjectMeta) bool { + if obj.Annotations[IsDefaultStorageClassAnnotation] == "true" { + return true + } + if obj.Annotations[BetaIsDefaultStorageClassAnnotation] == "true" { + return true + } + + return false +} diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/describe.go b/vendor/k8s.io/kubernetes/pkg/kubectl/describe.go index 5d820411df5a..95a653f97b1d 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/describe.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/describe.go @@ -40,6 +40,8 @@ import ( "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/certificates" "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" + storageutil "k8s.io/kubernetes/pkg/apis/storage/util" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" client "k8s.io/kubernetes/pkg/client/unversioned" adapter "k8s.io/kubernetes/pkg/client/unversioned/adapters/internalclientset" @@ -118,6 +120,7 @@ func describerMap(c *client.Client) map[unversioned.GroupKind]Describer { apps.Kind("PetSet"): &PetSetDescriber{c}, certificates.Kind("CertificateSigningRequest"): &CertificateSigningRequestDescriber{c}, api.Kind("SecurityContextConstraints"): &SecurityContextConstraintsDescriber{c}, + storage.Kind("StorageClass"): &StorageClassDescriber{c}, } return m @@ -896,6 +899,7 @@ func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSe return tabbedString(func(out io.Writer) error { fmt.Fprintf(out, "Name:\t%s\n", pv.Name) printLabelsMultiline(out, "Labels", pv.Labels) + fmt.Fprintf(out, "StorageClass:\t%s\n", storageutil.GetStorageClassAnnotation(pv.ObjectMeta)) fmt.Fprintf(out, "Status:\t%s\n", pv.Status.Phase) if pv.Spec.ClaimRef != nil { fmt.Fprintf(out, "Claim:\t%s\n", pv.Spec.ClaimRef.Namespace+"/"+pv.Spec.ClaimRef.Name) @@ -961,6 +965,7 @@ func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string, descri return tabbedString(func(out io.Writer) error { fmt.Fprintf(out, "Name:\t%s\n", pvc.Name) fmt.Fprintf(out, "Namespace:\t%s\n", pvc.Namespace) + fmt.Fprintf(out, "StorageClass:\t%s\n", storageutil.GetStorageClassAnnotation(pvc.ObjectMeta)) fmt.Fprintf(out, "Status:\t%v\n", pvc.Status.Phase) fmt.Fprintf(out, "Volume:\t%s\n", pvc.Spec.VolumeName) printLabelsMultiline(out, "Labels", pvc.Labels) @@ -2511,6 +2516,34 @@ func describeNetworkPolicy(networkPolicy *extensions.NetworkPolicy) (string, err }) } +type StorageClassDescriber struct { + client.Interface +} + +func (s *StorageClassDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { + sc, err := s.Storage().StorageClasses().Get(name) + if err != nil { + return "", err + } + return tabbedString(func(out io.Writer) error { + fmt.Fprintf(out, "Name:\t%s\n", sc.Name) + fmt.Fprintf(out, "IsDefaultClass:\t%s\n", storageutil.IsDefaultAnnotationText(sc.ObjectMeta)) + fmt.Fprintf(out, "Annotations:\t%s\n", labels.FormatLabels(sc.Annotations)) + fmt.Fprintf(out, "Provisioner:\t%s\n", sc.Provisioner) + fmt.Fprintf(out, "Parameters:\t%s\n", labels.FormatLabels(sc.Parameters)) + if describerSettings.ShowEvents { + events, err := s.Events(namespace).Search(sc) + if err != nil { + return err + } + if events != nil { + DescribeEvents(events, out) + } + } + return nil + }) +} + // newErrNoDescriber creates a new ErrNoDescriber with the names of the provided types. func newErrNoDescriber(types ...reflect.Type) error { names := make([]string, 0, len(types)) diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/describe_test.go b/vendor/k8s.io/kubernetes/pkg/kubectl/describe_test.go index ccf11a6285bc..23ef4cb0eb62 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/describe_test.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/describe_test.go @@ -31,6 +31,7 @@ import ( "k8s.io/kubernetes/pkg/api/resource" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/apis/storage" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/client/unversioned/testclient" @@ -623,6 +624,31 @@ func TestDescribeCluster(t *testing.T) { } } +func TestDescribeStorageClass(t *testing.T) { + f := testclient.NewSimpleFake(&storage.StorageClass{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + ResourceVersion: "4", + Annotations: map[string]string{ + "name": "foo", + }, + }, + Provisioner: "my-provisioner", + Parameters: map[string]string{ + "param1": "value1", + "param2": "value2", + }, + }) + s := StorageClassDescriber{f} + out, err := s.Describe("", "foo", DescriberSettings{ShowEvents: true}) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if !strings.Contains(out, "foo") { + t.Errorf("unexpected out: %s", out) + } +} + func TestDescribeEvents(t *testing.T) { events := &api.EventList{ @@ -721,6 +747,14 @@ func TestDescribeEvents(t *testing.T) { }, }, events), }, + "StorageClass": &StorageClassDescriber{ + testclient.NewSimpleFake(&storage.StorageClass{ + ObjectMeta: api.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + }, events), + }, } for name, d := range m { diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/resource_printer.go b/vendor/k8s.io/kubernetes/pkg/kubectl/resource_printer.go index 0b076106e31a..1fa63b561543 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/resource_printer.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/resource_printer.go @@ -43,6 +43,7 @@ import ( "k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/storage" + storageutil "k8s.io/kubernetes/pkg/apis/storage/util" "k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/runtime" utilerrors "k8s.io/kubernetes/pkg/util/errors" @@ -2056,6 +2057,10 @@ func printNetworkPolicyList(list *extensions.NetworkPolicyList, w io.Writer, opt func printStorageClass(sc *storage.StorageClass, w io.Writer, options PrintOptions) error { name := sc.Name + + if storageutil.IsDefaultAnnotation(sc.ObjectMeta) { + name += " (default)" + } provtype := sc.Provisioner if _, err := fmt.Fprintf(w, "%s\t%s\t", name, provtype); err != nil {