1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package client 18 19 import ( 20 "context" 21 22 apierrors "k8s.io/apimachinery/pkg/api/errors" 23 "k8s.io/apimachinery/pkg/runtime/schema" 24 25 "k8s.io/apimachinery/pkg/api/meta" 26 "k8s.io/apimachinery/pkg/runtime" 27 "k8s.io/apimachinery/pkg/types" 28 "k8s.io/apimachinery/pkg/watch" 29 ) 30 31 // ObjectKey identifies a Kubernetes Object. 32 type ObjectKey = types.NamespacedName 33 34 // ObjectKeyFromObject returns the ObjectKey given a runtime.Object. 35 func ObjectKeyFromObject(obj Object) ObjectKey { 36 return ObjectKey{Namespace: obj.GetNamespace(), Name: obj.GetName()} 37 } 38 39 // Patch is a patch that can be applied to a Kubernetes object. 40 type Patch interface { 41 // Type is the PatchType of the patch. 42 Type() types.PatchType 43 // Data is the raw data representing the patch. 44 Data(obj Object) ([]byte, error) 45 } 46 47 // TODO(directxman12): is there a sane way to deal with get/delete options? 48 49 // Reader knows how to read and list Kubernetes objects. 50 type Reader interface { 51 // Get retrieves an obj for the given object key from the Kubernetes Cluster. 52 // obj must be a struct pointer so that obj can be updated with the response 53 // returned by the Server. 54 Get(ctx context.Context, key ObjectKey, obj Object, opts ...GetOption) error 55 56 // List retrieves list of objects for a given namespace and list options. On a 57 // successful call, Items field in the list will be populated with the 58 // result returned from the server. 59 List(ctx context.Context, list ObjectList, opts ...ListOption) error 60 } 61 62 // Writer knows how to create, delete, and update Kubernetes objects. 63 type Writer interface { 64 // Create saves the object obj in the Kubernetes cluster. obj must be a 65 // struct pointer so that obj can be updated with the content returned by the Server. 66 Create(ctx context.Context, obj Object, opts ...CreateOption) error 67 68 // Delete deletes the given obj from Kubernetes cluster. 69 Delete(ctx context.Context, obj Object, opts ...DeleteOption) error 70 71 // Update updates the given obj in the Kubernetes cluster. obj must be a 72 // struct pointer so that obj can be updated with the content returned by the Server. 73 Update(ctx context.Context, obj Object, opts ...UpdateOption) error 74 75 // Patch patches the given obj in the Kubernetes cluster. obj must be a 76 // struct pointer so that obj can be updated with the content returned by the Server. 77 Patch(ctx context.Context, obj Object, patch Patch, opts ...PatchOption) error 78 79 // DeleteAllOf deletes all objects of the given type matching the given options. 80 DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error 81 } 82 83 // StatusClient knows how to create a client which can update status subresource 84 // for kubernetes objects. 85 type StatusClient interface { 86 Status() SubResourceWriter 87 } 88 89 // SubResourceClientConstructor knows how to create a client which can update subresource 90 // for kubernetes objects. 91 type SubResourceClientConstructor interface { 92 // SubResourceClientConstructor returns a subresource client for the named subResource. Known 93 // upstream subResources usages are: 94 // - ServiceAccount token creation: 95 // sa := &corev1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"}} 96 // token := &authenticationv1.TokenRequest{} 97 // c.SubResourceClient("token").Create(ctx, sa, token) 98 // 99 // - Pod eviction creation: 100 // pod := &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"}} 101 // c.SubResourceClient("eviction").Create(ctx, pod, &policyv1.Eviction{}) 102 // 103 // - Pod binding creation: 104 // pod := &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"}} 105 // binding := &corev1.Binding{Target: corev1.ObjectReference{Name: "my-node"}} 106 // c.SubResourceClient("binding").Create(ctx, pod, binding) 107 // 108 // - CertificateSigningRequest approval: 109 // csr := &certificatesv1.CertificateSigningRequest{ 110 // ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"}, 111 // Status: certificatesv1.CertificateSigningRequestStatus{ 112 // Conditions: []certificatesv1.[]CertificateSigningRequestCondition{{ 113 // Type: certificatesv1.CertificateApproved, 114 // Status: corev1.ConditionTrue, 115 // }}, 116 // }, 117 // } 118 // c.SubResourceClient("approval").Update(ctx, csr) 119 // 120 // - Scale retrieval: 121 // dep := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"}} 122 // scale := &autoscalingv1.Scale{} 123 // c.SubResourceClient("scale").Get(ctx, dep, scale) 124 // 125 // - Scale update: 126 // dep := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Namespace: "foo", Name: "bar"}} 127 // scale := &autoscalingv1.Scale{Spec: autoscalingv1.ScaleSpec{Replicas: 2}} 128 // c.SubResourceClient("scale").Update(ctx, dep, client.WithSubResourceBody(scale)) 129 SubResource(subResource string) SubResourceClient 130 } 131 132 // StatusWriter is kept for backward compatibility. 133 type StatusWriter = SubResourceWriter 134 135 // SubResourceReader knows how to read SubResources 136 type SubResourceReader interface { 137 Get(ctx context.Context, obj Object, subResource Object, opts ...SubResourceGetOption) error 138 } 139 140 // SubResourceWriter knows how to update subresource of a Kubernetes object. 141 type SubResourceWriter interface { 142 // Create saves the subResource object in the Kubernetes cluster. obj must be a 143 // struct pointer so that obj can be updated with the content returned by the Server. 144 Create(ctx context.Context, obj Object, subResource Object, opts ...SubResourceCreateOption) error 145 146 // Update updates the fields corresponding to the status subresource for the 147 // given obj. obj must be a struct pointer so that obj can be updated 148 // with the content returned by the Server. 149 Update(ctx context.Context, obj Object, opts ...SubResourceUpdateOption) error 150 151 // Patch patches the given object's subresource. obj must be a struct 152 // pointer so that obj can be updated with the content returned by the 153 // Server. 154 Patch(ctx context.Context, obj Object, patch Patch, opts ...SubResourcePatchOption) error 155 } 156 157 // SubResourceClient knows how to perform CRU operations on Kubernetes objects. 158 type SubResourceClient interface { 159 SubResourceReader 160 SubResourceWriter 161 } 162 163 // Client knows how to perform CRUD operations on Kubernetes objects. 164 type Client interface { 165 Reader 166 Writer 167 StatusClient 168 SubResourceClientConstructor 169 170 // Scheme returns the scheme this client is using. 171 Scheme() *runtime.Scheme 172 // RESTMapper returns the rest this client is using. 173 RESTMapper() meta.RESTMapper 174 // GroupVersionKindFor returns the GroupVersionKind for the given object. 175 GroupVersionKindFor(obj runtime.Object) (schema.GroupVersionKind, error) 176 // IsObjectNamespaced returns true if the GroupVersionKind of the object is namespaced. 177 IsObjectNamespaced(obj runtime.Object) (bool, error) 178 } 179 180 // WithWatch supports Watch on top of the CRUD operations supported by 181 // the normal Client. Its intended use-case are CLI apps that need to wait for 182 // events. 183 type WithWatch interface { 184 Client 185 Watch(ctx context.Context, obj ObjectList, opts ...ListOption) (watch.Interface, error) 186 } 187 188 // IndexerFunc knows how to take an object and turn it into a series 189 // of non-namespaced keys. Namespaced objects are automatically given 190 // namespaced and non-spaced variants, so keys do not need to include namespace. 191 type IndexerFunc func(Object) []string 192 193 // FieldIndexer knows how to index over a particular "field" such that it 194 // can later be used by a field selector. 195 type FieldIndexer interface { 196 // IndexFields adds an index with the given field name on the given object type 197 // by using the given function to extract the value for that field. If you want 198 // compatibility with the Kubernetes API server, only return one key, and only use 199 // fields that the API server supports. Otherwise, you can return multiple keys, 200 // and "equality" in the field selector means that at least one key matches the value. 201 // The FieldIndexer will automatically take care of indexing over namespace 202 // and supporting efficient all-namespace queries. 203 IndexField(ctx context.Context, obj Object, field string, extractValue IndexerFunc) error 204 } 205 206 // IgnoreNotFound returns nil on NotFound errors. 207 // All other values that are not NotFound errors or nil are returned unmodified. 208 func IgnoreNotFound(err error) error { 209 if apierrors.IsNotFound(err) { 210 return nil 211 } 212 return err 213 } 214 215 // IgnoreAlreadyExists returns nil on AlreadyExists errors. 216 // All other values that are not AlreadyExists errors or nil are returned unmodified. 217 func IgnoreAlreadyExists(err error) error { 218 if apierrors.IsAlreadyExists(err) { 219 return nil 220 } 221 222 return err 223 } 224