├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── apis ├── admission │ └── v1beta1 │ │ └── generated.pb.go ├── admissionregistration │ ├── v1alpha1 │ │ ├── generated.pb.go │ │ └── register.go │ └── v1beta1 │ │ ├── generated.pb.go │ │ └── register.go ├── apiextensions │ └── v1beta1 │ │ ├── generated.pb.go │ │ └── register.go ├── apiregistration │ ├── v1 │ │ ├── generated.pb.go │ │ └── register.go │ └── v1beta1 │ │ ├── generated.pb.go │ │ └── register.go ├── apps │ ├── v1 │ │ ├── generated.pb.go │ │ └── register.go │ ├── v1beta1 │ │ ├── generated.pb.go │ │ └── register.go │ └── v1beta2 │ │ ├── generated.pb.go │ │ └── register.go ├── auditregistration │ └── v1alpha1 │ │ └── generated.pb.go ├── authentication │ ├── v1 │ │ ├── generated.pb.go │ │ └── register.go │ └── v1beta1 │ │ ├── generated.pb.go │ │ └── register.go ├── authorization │ ├── v1 │ │ ├── generated.pb.go │ │ └── register.go │ └── v1beta1 │ │ ├── generated.pb.go │ │ └── register.go ├── autoscaling │ ├── v1 │ │ ├── generated.pb.go │ │ └── register.go │ ├── v2beta1 │ │ ├── generated.pb.go │ │ └── register.go │ └── v2beta2 │ │ └── generated.pb.go ├── batch │ ├── v1 │ │ ├── generated.pb.go │ │ └── register.go │ ├── v1beta1 │ │ ├── generated.pb.go │ │ └── register.go │ └── v2alpha1 │ │ ├── generated.pb.go │ │ └── register.go ├── certificates │ └── v1beta1 │ │ ├── generated.pb.go │ │ └── register.go ├── coordination │ └── v1beta1 │ │ └── generated.pb.go ├── core │ └── v1 │ │ ├── generated.pb.go │ │ └── register.go ├── events │ └── v1beta1 │ │ ├── generated.pb.go │ │ └── register.go ├── extensions │ └── v1beta1 │ │ ├── generated.pb.go │ │ └── register.go ├── imagepolicy │ └── v1alpha1 │ │ └── generated.pb.go ├── meta │ ├── v1 │ │ ├── generated.pb.go │ │ └── json.go │ └── v1beta1 │ │ └── generated.pb.go ├── networking │ └── v1 │ │ ├── generated.pb.go │ │ └── register.go ├── policy │ └── v1beta1 │ │ ├── generated.pb.go │ │ └── register.go ├── rbac │ ├── v1 │ │ ├── generated.pb.go │ │ └── register.go │ ├── v1alpha1 │ │ ├── generated.pb.go │ │ └── register.go │ └── v1beta1 │ │ ├── generated.pb.go │ │ └── register.go ├── resource │ └── generated.pb.go ├── scheduling │ ├── v1alpha1 │ │ ├── generated.pb.go │ │ └── register.go │ └── v1beta1 │ │ └── generated.pb.go ├── settings │ └── v1alpha1 │ │ ├── generated.pb.go │ │ └── register.go └── storage │ ├── v1 │ ├── generated.pb.go │ └── register.go │ ├── v1alpha1 │ ├── generated.pb.go │ └── register.go │ └── v1beta1 │ ├── generated.pb.go │ └── register.go ├── client.go ├── client_test.go ├── codec.go ├── config.go ├── discovery.go ├── discovery_test.go ├── examples ├── api-errors.go ├── create-resource.go ├── custom-resources.go ├── in-cluster-client.go └── out-of-cluster-client.go ├── go.mod ├── go.sum ├── labels.go ├── labels_test.go ├── resource.go ├── resource_test.go ├── runtime ├── generated.pb.go └── schema │ └── generated.pb.go ├── scripts ├── generate.sh ├── get-protoc.sh ├── git-diff.sh ├── go-install.sh ├── json.go.partial ├── kubeconfig ├── register.go └── run-kube.sh ├── util └── intstr │ └── generated.pb.go ├── watch.go └── watch_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | assets 2 | _output/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | sudo: required 4 | 5 | go: 6 | - '1.10' 7 | - '1.11' 8 | 9 | services: 10 | - docker 11 | 12 | addons: 13 | apt: 14 | packages: 15 | - bsdtar 16 | 17 | env: 18 | - K8S_CLIENT_TEST=1 KUBECONFIG=scripts/kubeconfig 19 | 20 | install: 21 | - go get -v ./... 22 | - go get -v github.com/ghodss/yaml # Required for examples. 23 | - ./scripts/run-kube.sh 24 | - curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.9.1/bin/linux/amd64/kubectl 25 | - chmod +x kubectl 26 | - mv kubectl $GOPATH/bin 27 | 28 | script: 29 | - make 30 | - make test 31 | - make test-examples 32 | - make verify-generate 33 | 34 | 35 | notifications: 36 | email: false 37 | 38 | branches: 39 | only: 40 | - master 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | KUBE_VERSION=1.13.2 2 | 3 | build: 4 | go build -v ./... 5 | 6 | test: 7 | go test -v ./... 8 | 9 | test-examples: 10 | @for example in $(shell find examples/ -name '*.go'); do \ 11 | go build -v $$example || exit 1; \ 12 | done 13 | 14 | .PHONY: generate 15 | generate: _output/kubernetes _output/bin/protoc _output/bin/gomvpkg _output/bin/protoc-gen-gofast _output/src/github.com/golang/protobuf 16 | GO111MODULE=off ./scripts/generate.sh 17 | GO111MODULE=off go run scripts/register.go 18 | cp scripts/json.go.partial apis/meta/v1/json.go 19 | 20 | .PHONY: verify-generate 21 | verify-generate: generate 22 | ./scripts/git-diff.sh 23 | 24 | _output/bin/protoc-gen-gofast: 25 | GO111MODULE=off ./scripts/go-install.sh \ 26 | https://github.com/gogo/protobuf \ 27 | github.com/gogo/protobuf \ 28 | github.com/gogo/protobuf/protoc-gen-gofast \ 29 | tags/v0.5 30 | 31 | _output/bin/gomvpkg: 32 | GO111MODULE=off ./scripts/go-install.sh \ 33 | https://github.com/golang/tools \ 34 | golang.org/x/tools \ 35 | golang.org/x/tools/cmd/gomvpkg \ 36 | fbec762f837dc349b73d1eaa820552e2ad177942 37 | 38 | _output/src/github.com/golang/protobuf: 39 | git clone https://github.com/golang/protobuf _output/src/github.com/golang/protobuf 40 | 41 | _output/bin/protoc: 42 | ./scripts/get-protoc.sh 43 | 44 | _output/kubernetes: 45 | mkdir -p _output 46 | curl -o _output/kubernetes.zip -L https://github.com/kubernetes/kubernetes/archive/v$(KUBE_VERSION).zip 47 | bsdtar -x -f _output/kubernetes.zip -C _output > /dev/null 48 | mv _output/kubernetes-$(KUBE_VERSION) _output/kubernetes 49 | 50 | .PHONY: clean 51 | clean: 52 | rm -rf _output 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A simple Go client for Kubernetes 2 | 3 | [![GoDoc](https://godoc.org/github.com/ericchiang/k8s?status.svg)](https://godoc.org/github.com/ericchiang/k8s) 4 | [![Build Status](https://travis-ci.org/ericchiang/k8s.svg?branch=master)](https://travis-ci.org/ericchiang/k8s) 5 | 6 | A slimmed down Go client generated using Kubernetes' [protocol buffer][protobuf] support. This package behaves similarly to [official Kubernetes' Go client][client-go], but only imports two external dependencies. 7 | 8 | ```go 9 | package main 10 | 11 | import ( 12 | "context" 13 | "fmt" 14 | "log" 15 | 16 | "github.com/ericchiang/k8s" 17 | corev1 "github.com/ericchiang/k8s/apis/core/v1" 18 | ) 19 | 20 | func main() { 21 | client, err := k8s.NewInClusterClient() 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | 26 | var nodes corev1.NodeList 27 | if err := client.List(context.Background(), "", &nodes); err != nil { 28 | log.Fatal(err) 29 | } 30 | for _, node := range nodes.Items { 31 | fmt.Printf("name=%q schedulable=%t\n", *node.Metadata.Name, !*node.Spec.Unschedulable) 32 | } 33 | } 34 | ``` 35 | 36 | ## Requirements 37 | 38 | * Go 1.7+ (this package uses "context" features added in 1.7) 39 | * Kubernetes 1.3+ (protobuf support was added in 1.3) 40 | * [github.com/golang/protobuf/proto][go-proto] (protobuf serialization) 41 | * [golang.org/x/net/http2][go-http2] (HTTP/2 support) 42 | 43 | ## Usage 44 | 45 | ### Create, update, delete 46 | 47 | The type of the object passed to `Create`, `Update`, and `Delete` determine the resource being acted on. 48 | 49 | ```go 50 | configMap := &corev1.ConfigMap{ 51 | Metadata: &metav1.ObjectMeta{ 52 | Name: k8s.String("my-configmap"), 53 | Namespace: k8s.String("my-namespace"), 54 | }, 55 | Data: map[string]string{"hello": "world"}, 56 | } 57 | 58 | if err := client.Create(ctx, configMap); err != nil { 59 | // handle error 60 | } 61 | 62 | configMap.Data["hello"] = "kubernetes" 63 | 64 | if err := client.Update(ctx, configMap); err != nil { 65 | // handle error 66 | } 67 | 68 | if err := client.Delete(ctx, configMap); err != nil { 69 | // handle error 70 | } 71 | ``` 72 | 73 | ### Get, list, watch 74 | 75 | Getting a resource requires providing a namespace (for namespaced objects) and a name. 76 | 77 | ```go 78 | // Get the "cluster-info" configmap from the "kube-public" namespace 79 | var configMap corev1.ConfigMap 80 | err := client.Get(ctx, "kube-public", "cluster-info", &configMap) 81 | ``` 82 | 83 | When performing a list operation, the namespace to list or watch is also required. 84 | 85 | ```go 86 | // Pods from the "custom-namespace" 87 | var pods corev1.PodList 88 | err := client.List(ctx, "custom-namespace", &pods) 89 | ``` 90 | 91 | A special value `AllNamespaces` indicates that the list or watch should be performed on all cluster resources. 92 | 93 | ```go 94 | // Pods in all namespaces 95 | var pods corev1.PodList 96 | err := client.List(ctx, k8s.AllNamespaces, &pods) 97 | ``` 98 | 99 | Watches require a example type to determine what resource they're watching. `Watch` returns an type which can be used to receive a stream of events. These events include resources of the same kind and the kind of the event (added, modified, deleted). 100 | 101 | ```go 102 | // Watch configmaps in the "kube-system" namespace 103 | var configMap corev1.ConfigMap 104 | watcher, err := client.Watch(ctx, "kube-system", &configMap) 105 | if err != nil { 106 | // handle error 107 | } 108 | defer watcher.Close() 109 | 110 | for { 111 | cm := new(corev1.ConfigMap) 112 | eventType, err := watcher.Next(cm) 113 | if err != nil { 114 | // watcher encountered and error, exit or create a new watcher 115 | } 116 | fmt.Println(eventType, *cm.Metadata.Name) 117 | } 118 | ``` 119 | 120 | Both in-cluster and out-of-cluster clients are initialized with a primary namespace. This is the recommended value to use when listing or watching. 121 | 122 | ```go 123 | client, err := k8s.NewInClusterClient() 124 | if err != nil { 125 | // handle error 126 | } 127 | 128 | // List pods in the namespace the client is running in. 129 | var pods corev1.PodList 130 | err := client.List(ctx, client.Namespace, &pods) 131 | ``` 132 | 133 | ### Custom resources 134 | 135 | Client operations support user defined resources, such as resources provided by [CustomResourceDefinitions][crds] and [aggregated API servers][custom-api-servers]. To use a custom resource, define an equivalent Go struct then register it with the `k8s` package. By default the client will use JSON serialization when encoding and decoding custom resources. 136 | 137 | ```go 138 | import ( 139 | "github.com/ericchiang/k8s" 140 | metav1 "github.com/ericchiang/k8s/apis/meta/v1" 141 | ) 142 | 143 | type MyResource struct { 144 | Metadata *metav1.ObjectMeta `json:"metadata"` 145 | Foo string `json:"foo"` 146 | Bar int `json:"bar"` 147 | } 148 | 149 | // Required for MyResource to implement k8s.Resource 150 | func (m *MyResource) GetMetadata() *metav1.ObjectMeta { 151 | return m.Metadata 152 | } 153 | 154 | type MyResourceList struct { 155 | Metadata *metav1.ListMeta `json:"metadata"` 156 | Items []MyResource `json:"items"` 157 | } 158 | 159 | // Require for MyResourceList to implement k8s.ResourceList 160 | func (m *MyResourceList) GetMetadata() *metav1.ListMeta { 161 | return m.Metadata 162 | } 163 | 164 | func init() { 165 | // Register resources with the k8s package. 166 | k8s.Register("resource.example.com", "v1", "myresources", true, &MyResource{}) 167 | k8s.RegisterList("resource.example.com", "v1", "myresources", true, &MyResourceList{}) 168 | } 169 | ``` 170 | 171 | Once registered, the library can use the custom resources like any other. 172 | 173 | ```go 174 | func do(ctx context.Context, client *k8s.Client, namespace string) error { 175 | r := &MyResource{ 176 | Metadata: &metav1.ObjectMeta{ 177 | Name: k8s.String("my-custom-resource"), 178 | Namespace: &namespace, 179 | }, 180 | Foo: "hello, world!", 181 | Bar: 42, 182 | } 183 | if err := client.Create(ctx, r); err != nil { 184 | return fmt.Errorf("create: %v", err) 185 | } 186 | r.Bar = -8 187 | if err := client.Update(ctx, r); err != nil { 188 | return fmt.Errorf("update: %v", err) 189 | } 190 | if err := client.Delete(ctx, r); err != nil { 191 | return fmt.Errorf("delete: %v", err) 192 | } 193 | return nil 194 | } 195 | ``` 196 | 197 | If the custom type implements [`proto.Message`][proto-msg], the client will prefer protobuf when encoding and decoding the type. 198 | 199 | ### Label selectors 200 | 201 | Label selectors can be provided to any list operation. 202 | 203 | ```go 204 | l := new(k8s.LabelSelector) 205 | l.Eq("tier", "production") 206 | l.In("app", "database", "frontend") 207 | 208 | var pods corev1.PodList 209 | err := client.List(ctx, "custom-namespace", &pods, l.Selector()) 210 | ``` 211 | 212 | ### Subresources 213 | 214 | Access subresources using the `Subresource` option. 215 | 216 | ```go 217 | err := client.Update(ctx, &pod, k8s.Subresource("status")) 218 | ``` 219 | 220 | ### Creating out-of-cluster clients 221 | 222 | Out-of-cluster clients can be constructed by either creating an `http.Client` manually or parsing a [`Config`][config] object. The following is an example of creating a client from a kubeconfig: 223 | 224 | ```go 225 | import ( 226 | "io/ioutil" 227 | 228 | "github.com/ericchiang/k8s" 229 | 230 | "github.com/ghodss/yaml" 231 | ) 232 | 233 | // loadClient parses a kubeconfig from a file and returns a Kubernetes 234 | // client. It does not support extensions or client auth providers. 235 | func loadClient(kubeconfigPath string) (*k8s.Client, error) { 236 | data, err := ioutil.ReadFile(kubeconfigPath) 237 | if err != nil { 238 | return nil, fmt.Errorf("read kubeconfig: %v", err) 239 | } 240 | 241 | // Unmarshal YAML into a Kubernetes config object. 242 | var config k8s.Config 243 | if err := yaml.Unmarshal(data, &config); err != nil { 244 | return nil, fmt.Errorf("unmarshal kubeconfig: %v", err) 245 | } 246 | return k8s.NewClient(&config) 247 | } 248 | ``` 249 | 250 | ### Errors 251 | 252 | Errors returned by the Kubernetes API are formatted as [`unversioned.Status`][unversioned-status] objects and surfaced by clients as [`*k8s.APIError`][k8s-error]s. Programs that need to inspect error codes or failure details can use a type cast to access this information. 253 | 254 | ```go 255 | // createConfigMap creates a configmap in the client's default namespace 256 | // but does not return an error if a configmap of the same name already 257 | // exists. 258 | func createConfigMap(client *k8s.Client, name string, values map[string]string) error { 259 | cm := &v1.ConfigMap{ 260 | Metadata: &metav1.ObjectMeta{ 261 | Name: &name, 262 | Namespace: &client.Namespace, 263 | }, 264 | Data: values, 265 | } 266 | 267 | err := client.Create(context.TODO(), cm) 268 | 269 | // If an HTTP error was returned by the API server, it will be of type 270 | // *k8s.APIError. This can be used to inspect the status code. 271 | if apiErr, ok := err.(*k8s.APIError); ok { 272 | // Resource already exists. Carry on. 273 | if apiErr.Code == http.StatusConflict { 274 | return nil 275 | } 276 | } 277 | return fmt.Errorf("create configmap: %v", err) 278 | } 279 | ``` 280 | 281 | [client-go]: https://github.com/kubernetes/client-go 282 | [go-proto]: https://godoc.org/github.com/golang/protobuf/proto 283 | [go-http2]: https://godoc.org/golang.org/x/net/http2 284 | [protobuf]: https://developers.google.com/protocol-buffers/ 285 | [unversioned-status]: https://godoc.org/github.com/ericchiang/k8s/api/unversioned#Status 286 | [k8s-error]: https://godoc.org/github.com/ericchiang/k8s#APIError 287 | [config]: https://godoc.org/github.com/ericchiang/k8s#Config 288 | [string]: https://godoc.org/github.com/ericchiang/k8s#String 289 | [crds]: https://kubernetes.io/docs/tasks/access-kubernetes-api/extend-api-custom-resource-definitions/ 290 | [custom-api-servers]: https://kubernetes.io/docs/concepts/api-extension/apiserver-aggregation/ 291 | [proto-msg]: https://godoc.org/github.com/golang/protobuf/proto#Message 292 | -------------------------------------------------------------------------------- /apis/admissionregistration/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("admissionregistration.k8s.io", "v1alpha1", "initializerconfigurations", false, &InitializerConfiguration{}) 7 | 8 | k8s.RegisterList("admissionregistration.k8s.io", "v1alpha1", "initializerconfigurations", false, &InitializerConfigurationList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/admissionregistration/v1beta1/register.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("admissionregistration.k8s.io", "v1beta1", "mutatingwebhookconfigurations", false, &MutatingWebhookConfiguration{}) 7 | k8s.Register("admissionregistration.k8s.io", "v1beta1", "validatingwebhookconfigurations", false, &ValidatingWebhookConfiguration{}) 8 | 9 | k8s.RegisterList("admissionregistration.k8s.io", "v1beta1", "mutatingwebhookconfigurations", false, &MutatingWebhookConfigurationList{}) 10 | k8s.RegisterList("admissionregistration.k8s.io", "v1beta1", "validatingwebhookconfigurations", false, &ValidatingWebhookConfigurationList{}) 11 | } 12 | -------------------------------------------------------------------------------- /apis/apiextensions/v1beta1/register.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("apiextensions.k8s.io", "v1beta1", "customresourcedefinitions", false, &CustomResourceDefinition{}) 7 | 8 | k8s.RegisterList("apiextensions.k8s.io", "v1beta1", "customresourcedefinitions", false, &CustomResourceDefinitionList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/apiregistration/v1/register.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("apiregistration.k8s.io", "v1", "apiservices", false, &APIService{}) 7 | 8 | k8s.RegisterList("apiregistration.k8s.io", "v1", "apiservices", false, &APIServiceList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/apiregistration/v1beta1/register.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("apiregistration.k8s.io", "v1beta1", "apiservices", false, &APIService{}) 7 | 8 | k8s.RegisterList("apiregistration.k8s.io", "v1beta1", "apiservices", false, &APIServiceList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/apps/v1/register.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("apps", "v1", "controllerrevisions", true, &ControllerRevision{}) 7 | k8s.Register("apps", "v1", "daemonsets", true, &DaemonSet{}) 8 | k8s.Register("apps", "v1", "deployments", true, &Deployment{}) 9 | k8s.Register("apps", "v1", "replicasets", true, &ReplicaSet{}) 10 | k8s.Register("apps", "v1", "statefulsets", true, &StatefulSet{}) 11 | 12 | k8s.RegisterList("apps", "v1", "controllerrevisions", true, &ControllerRevisionList{}) 13 | k8s.RegisterList("apps", "v1", "daemonsets", true, &DaemonSetList{}) 14 | k8s.RegisterList("apps", "v1", "deployments", true, &DeploymentList{}) 15 | k8s.RegisterList("apps", "v1", "replicasets", true, &ReplicaSetList{}) 16 | k8s.RegisterList("apps", "v1", "statefulsets", true, &StatefulSetList{}) 17 | } 18 | -------------------------------------------------------------------------------- /apis/apps/v1beta1/register.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("apps", "v1beta1", "controllerrevisions", true, &ControllerRevision{}) 7 | k8s.Register("apps", "v1beta1", "deployments", true, &Deployment{}) 8 | k8s.Register("apps", "v1beta1", "statefulsets", true, &StatefulSet{}) 9 | 10 | k8s.RegisterList("apps", "v1beta1", "controllerrevisions", true, &ControllerRevisionList{}) 11 | k8s.RegisterList("apps", "v1beta1", "deployments", true, &DeploymentList{}) 12 | k8s.RegisterList("apps", "v1beta1", "statefulsets", true, &StatefulSetList{}) 13 | } 14 | -------------------------------------------------------------------------------- /apis/apps/v1beta2/register.go: -------------------------------------------------------------------------------- 1 | package v1beta2 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("apps", "v1beta2", "controllerrevisions", true, &ControllerRevision{}) 7 | k8s.Register("apps", "v1beta2", "daemonsets", true, &DaemonSet{}) 8 | k8s.Register("apps", "v1beta2", "deployments", true, &Deployment{}) 9 | k8s.Register("apps", "v1beta2", "replicasets", true, &ReplicaSet{}) 10 | k8s.Register("apps", "v1beta2", "statefulsets", true, &StatefulSet{}) 11 | 12 | k8s.RegisterList("apps", "v1beta2", "controllerrevisions", true, &ControllerRevisionList{}) 13 | k8s.RegisterList("apps", "v1beta2", "daemonsets", true, &DaemonSetList{}) 14 | k8s.RegisterList("apps", "v1beta2", "deployments", true, &DeploymentList{}) 15 | k8s.RegisterList("apps", "v1beta2", "replicasets", true, &ReplicaSetList{}) 16 | k8s.RegisterList("apps", "v1beta2", "statefulsets", true, &StatefulSetList{}) 17 | } 18 | -------------------------------------------------------------------------------- /apis/authentication/v1/register.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("authentication.k8s.io", "v1", "tokenreviews", false, &TokenReview{}) 7 | k8s.Register("authentication.k8s.io", "v1", "tokenrequests", false, &TokenRequest{}) 8 | } 9 | -------------------------------------------------------------------------------- /apis/authentication/v1beta1/register.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("authentication.k8s.io", "v1beta1", "tokenreviews", false, &TokenReview{}) 7 | } 8 | -------------------------------------------------------------------------------- /apis/authorization/v1/register.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("authorization.k8s.io", "v1", "localsubjectaccessreviews", true, &LocalSubjectAccessReview{}) 7 | k8s.Register("authorization.k8s.io", "v1", "selfsubjectaccessreviews", false, &SelfSubjectAccessReview{}) 8 | k8s.Register("authorization.k8s.io", "v1", "selfsubjectrulesreviews", false, &SelfSubjectRulesReview{}) 9 | k8s.Register("authorization.k8s.io", "v1", "subjectaccessreviews", false, &SubjectAccessReview{}) 10 | } 11 | -------------------------------------------------------------------------------- /apis/authorization/v1beta1/register.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("authorization.k8s.io", "v1beta1", "localsubjectaccessreviews", true, &LocalSubjectAccessReview{}) 7 | k8s.Register("authorization.k8s.io", "v1beta1", "selfsubjectaccessreviews", false, &SelfSubjectAccessReview{}) 8 | k8s.Register("authorization.k8s.io", "v1beta1", "selfsubjectrulesreviews", false, &SelfSubjectRulesReview{}) 9 | k8s.Register("authorization.k8s.io", "v1beta1", "subjectaccessreviews", false, &SubjectAccessReview{}) 10 | } 11 | -------------------------------------------------------------------------------- /apis/autoscaling/v1/register.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("autoscaling", "v1", "horizontalpodautoscalers", true, &HorizontalPodAutoscaler{}) 7 | 8 | k8s.RegisterList("autoscaling", "v1", "horizontalpodautoscalers", true, &HorizontalPodAutoscalerList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/autoscaling/v2beta1/register.go: -------------------------------------------------------------------------------- 1 | package v2beta1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("autoscaling", "v2beta1", "horizontalpodautoscalers", true, &HorizontalPodAutoscaler{}) 7 | 8 | k8s.RegisterList("autoscaling", "v2beta1", "horizontalpodautoscalers", true, &HorizontalPodAutoscalerList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/batch/v1/register.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("batch", "v1", "jobs", true, &Job{}) 7 | 8 | k8s.RegisterList("batch", "v1", "jobs", true, &JobList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/batch/v1beta1/register.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("batch", "v1beta1", "cronjobs", true, &CronJob{}) 7 | 8 | k8s.RegisterList("batch", "v1beta1", "cronjobs", true, &CronJobList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/batch/v2alpha1/register.go: -------------------------------------------------------------------------------- 1 | package v2alpha1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("batch", "v2alpha1", "cronjobs", true, &CronJob{}) 7 | 8 | k8s.RegisterList("batch", "v2alpha1", "cronjobs", true, &CronJobList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/certificates/v1beta1/register.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("certificates.k8s.io", "v1beta1", "certificatesigningrequests", false, &CertificateSigningRequest{}) 7 | 8 | k8s.RegisterList("certificates.k8s.io", "v1beta1", "certificatesigningrequests", false, &CertificateSigningRequestList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/core/v1/register.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("", "v1", "componentstatuses", false, &ComponentStatus{}) 7 | k8s.Register("", "v1", "configmaps", true, &ConfigMap{}) 8 | k8s.Register("", "v1", "endpoints", true, &Endpoints{}) 9 | k8s.Register("", "v1", "limitranges", true, &LimitRange{}) 10 | k8s.Register("", "v1", "namespaces", false, &Namespace{}) 11 | k8s.Register("", "v1", "nodes", false, &Node{}) 12 | k8s.Register("", "v1", "persistentvolumeclaims", true, &PersistentVolumeClaim{}) 13 | k8s.Register("", "v1", "persistentvolumes", false, &PersistentVolume{}) 14 | k8s.Register("", "v1", "pods", true, &Pod{}) 15 | k8s.Register("", "v1", "replicationcontrollers", true, &ReplicationController{}) 16 | k8s.Register("", "v1", "resourcequotas", true, &ResourceQuota{}) 17 | k8s.Register("", "v1", "secrets", true, &Secret{}) 18 | k8s.Register("", "v1", "services", true, &Service{}) 19 | k8s.Register("", "v1", "serviceaccounts", true, &ServiceAccount{}) 20 | k8s.Register("", "v1", "events", true, &Event{}) 21 | 22 | k8s.RegisterList("", "v1", "componentstatuses", false, &ComponentStatusList{}) 23 | k8s.RegisterList("", "v1", "configmaps", true, &ConfigMapList{}) 24 | k8s.RegisterList("", "v1", "endpoints", true, &EndpointsList{}) 25 | k8s.RegisterList("", "v1", "limitranges", true, &LimitRangeList{}) 26 | k8s.RegisterList("", "v1", "namespaces", false, &NamespaceList{}) 27 | k8s.RegisterList("", "v1", "nodes", false, &NodeList{}) 28 | k8s.RegisterList("", "v1", "persistentvolumeclaims", true, &PersistentVolumeClaimList{}) 29 | k8s.RegisterList("", "v1", "persistentvolumes", false, &PersistentVolumeList{}) 30 | k8s.RegisterList("", "v1", "pods", true, &PodList{}) 31 | k8s.RegisterList("", "v1", "replicationcontrollers", true, &ReplicationControllerList{}) 32 | k8s.RegisterList("", "v1", "resourcequotas", true, &ResourceQuotaList{}) 33 | k8s.RegisterList("", "v1", "secrets", true, &SecretList{}) 34 | k8s.RegisterList("", "v1", "services", true, &ServiceList{}) 35 | k8s.RegisterList("", "v1", "serviceaccounts", true, &ServiceAccountList{}) 36 | k8s.RegisterList("", "v1", "events", true, &EventList{}) 37 | } 38 | -------------------------------------------------------------------------------- /apis/events/v1beta1/register.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("events.k8s.io", "v1beta1", "events", true, &Event{}) 7 | 8 | k8s.RegisterList("events.k8s.io", "v1beta1", "events", true, &EventList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/extensions/v1beta1/register.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("extensions", "v1beta1", "daemonsets", true, &DaemonSet{}) 7 | k8s.Register("extensions", "v1beta1", "deployments", true, &Deployment{}) 8 | k8s.Register("extensions", "v1beta1", "ingresses", true, &Ingress{}) 9 | k8s.Register("extensions", "v1beta1", "networkpolicies", true, &NetworkPolicy{}) 10 | k8s.Register("extensions", "v1beta1", "podsecuritypolicies", false, &PodSecurityPolicy{}) 11 | k8s.Register("extensions", "v1beta1", "replicasets", true, &ReplicaSet{}) 12 | 13 | k8s.RegisterList("extensions", "v1beta1", "daemonsets", true, &DaemonSetList{}) 14 | k8s.RegisterList("extensions", "v1beta1", "deployments", true, &DeploymentList{}) 15 | k8s.RegisterList("extensions", "v1beta1", "ingresses", true, &IngressList{}) 16 | k8s.RegisterList("extensions", "v1beta1", "networkpolicies", true, &NetworkPolicyList{}) 17 | k8s.RegisterList("extensions", "v1beta1", "podsecuritypolicies", false, &PodSecurityPolicyList{}) 18 | k8s.RegisterList("extensions", "v1beta1", "replicasets", true, &ReplicaSetList{}) 19 | } 20 | -------------------------------------------------------------------------------- /apis/meta/v1/json.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | // JSON marshaling logic for the Time type so it can be used for custom 9 | // resources, which serialize to JSON. 10 | 11 | func (t Time) MarshalJSON() ([]byte, error) { 12 | var seconds, nanos int64 13 | if t.Seconds != nil { 14 | seconds = *t.Seconds 15 | } 16 | if t.Nanos != nil { 17 | nanos = int64(*t.Nanos) 18 | } 19 | return json.Marshal(time.Unix(seconds, nanos)) 20 | } 21 | 22 | func (t *Time) UnmarshalJSON(p []byte) error { 23 | var t1 time.Time 24 | if err := json.Unmarshal(p, &t1); err != nil { 25 | return err 26 | } 27 | seconds := t1.Unix() 28 | nanos := int32(t1.UnixNano()) 29 | t.Seconds = &seconds 30 | t.Nanos = &nanos 31 | return nil 32 | } 33 | 34 | // Status must implement json.Unmarshaler for the codec to deserialize a JSON 35 | // payload into it. 36 | // 37 | // See https://github.com/ericchiang/k8s/issues/82 38 | 39 | type jsonStatus Status 40 | 41 | func (s *Status) UnmarshalJSON(data []byte) error { 42 | var j jsonStatus 43 | if err := json.Unmarshal(data, &j); err != nil { 44 | return err 45 | } 46 | *s = Status(j) 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /apis/meta/v1beta1/generated.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: k8s.io/apimachinery/pkg/apis/meta/v1beta1/generated.proto 3 | 4 | /* 5 | Package v1beta1 is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | k8s.io/apimachinery/pkg/apis/meta/v1beta1/generated.proto 9 | 10 | It has these top-level messages: 11 | PartialObjectMetadata 12 | PartialObjectMetadataList 13 | TableOptions 14 | */ 15 | package v1beta1 16 | 17 | import proto "github.com/golang/protobuf/proto" 18 | import fmt "fmt" 19 | import math "math" 20 | import k8s_io_apimachinery_pkg_apis_meta_v1 "github.com/ericchiang/k8s/apis/meta/v1" 21 | import _ "github.com/ericchiang/k8s/runtime" 22 | import _ "github.com/ericchiang/k8s/runtime/schema" 23 | 24 | import io "io" 25 | 26 | // Reference imports to suppress errors if they are not otherwise used. 27 | var _ = proto.Marshal 28 | var _ = fmt.Errorf 29 | var _ = math.Inf 30 | 31 | // This is a compile-time assertion to ensure that this generated file 32 | // is compatible with the proto package it is being compiled against. 33 | // A compilation error at this line likely means your copy of the 34 | // proto package needs to be updated. 35 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 36 | 37 | // PartialObjectMetadata is a generic representation of any object with ObjectMeta. It allows clients 38 | // to get access to a particular ObjectMeta schema without knowing the details of the version. 39 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 40 | type PartialObjectMetadata struct { 41 | // Standard object's metadata. 42 | // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata 43 | // +optional 44 | Metadata *k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta `protobuf:"bytes,1,opt,name=metadata" json:"metadata,omitempty"` 45 | XXX_unrecognized []byte `json:"-"` 46 | } 47 | 48 | func (m *PartialObjectMetadata) Reset() { *m = PartialObjectMetadata{} } 49 | func (m *PartialObjectMetadata) String() string { return proto.CompactTextString(m) } 50 | func (*PartialObjectMetadata) ProtoMessage() {} 51 | func (*PartialObjectMetadata) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} } 52 | 53 | func (m *PartialObjectMetadata) GetMetadata() *k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta { 54 | if m != nil { 55 | return m.Metadata 56 | } 57 | return nil 58 | } 59 | 60 | // PartialObjectMetadataList contains a list of objects containing only their metadata 61 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 62 | type PartialObjectMetadataList struct { 63 | // items contains each of the included items. 64 | Items []*PartialObjectMetadata `protobuf:"bytes,1,rep,name=items" json:"items,omitempty"` 65 | XXX_unrecognized []byte `json:"-"` 66 | } 67 | 68 | func (m *PartialObjectMetadataList) Reset() { *m = PartialObjectMetadataList{} } 69 | func (m *PartialObjectMetadataList) String() string { return proto.CompactTextString(m) } 70 | func (*PartialObjectMetadataList) ProtoMessage() {} 71 | func (*PartialObjectMetadataList) Descriptor() ([]byte, []int) { 72 | return fileDescriptorGenerated, []int{1} 73 | } 74 | 75 | func (m *PartialObjectMetadataList) GetItems() []*PartialObjectMetadata { 76 | if m != nil { 77 | return m.Items 78 | } 79 | return nil 80 | } 81 | 82 | // TableOptions are used when a Table is requested by the caller. 83 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 84 | type TableOptions struct { 85 | // includeObject decides whether to include each object along with its columnar information. 86 | // Specifying "None" will return no object, specifying "Object" will return the full object contents, and 87 | // specifying "Metadata" (the default) will return the object's metadata in the PartialObjectMetadata kind 88 | // in version v1beta1 of the meta.k8s.io API group. 89 | IncludeObject *string `protobuf:"bytes,1,opt,name=includeObject" json:"includeObject,omitempty"` 90 | XXX_unrecognized []byte `json:"-"` 91 | } 92 | 93 | func (m *TableOptions) Reset() { *m = TableOptions{} } 94 | func (m *TableOptions) String() string { return proto.CompactTextString(m) } 95 | func (*TableOptions) ProtoMessage() {} 96 | func (*TableOptions) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{2} } 97 | 98 | func (m *TableOptions) GetIncludeObject() string { 99 | if m != nil && m.IncludeObject != nil { 100 | return *m.IncludeObject 101 | } 102 | return "" 103 | } 104 | 105 | func init() { 106 | proto.RegisterType((*PartialObjectMetadata)(nil), "k8s.io.apimachinery.pkg.apis.meta.v1beta1.PartialObjectMetadata") 107 | proto.RegisterType((*PartialObjectMetadataList)(nil), "k8s.io.apimachinery.pkg.apis.meta.v1beta1.PartialObjectMetadataList") 108 | proto.RegisterType((*TableOptions)(nil), "k8s.io.apimachinery.pkg.apis.meta.v1beta1.TableOptions") 109 | } 110 | func (m *PartialObjectMetadata) Marshal() (dAtA []byte, err error) { 111 | size := m.Size() 112 | dAtA = make([]byte, size) 113 | n, err := m.MarshalTo(dAtA) 114 | if err != nil { 115 | return nil, err 116 | } 117 | return dAtA[:n], nil 118 | } 119 | 120 | func (m *PartialObjectMetadata) MarshalTo(dAtA []byte) (int, error) { 121 | var i int 122 | _ = i 123 | var l int 124 | _ = l 125 | if m.Metadata != nil { 126 | dAtA[i] = 0xa 127 | i++ 128 | i = encodeVarintGenerated(dAtA, i, uint64(m.Metadata.Size())) 129 | n1, err := m.Metadata.MarshalTo(dAtA[i:]) 130 | if err != nil { 131 | return 0, err 132 | } 133 | i += n1 134 | } 135 | if m.XXX_unrecognized != nil { 136 | i += copy(dAtA[i:], m.XXX_unrecognized) 137 | } 138 | return i, nil 139 | } 140 | 141 | func (m *PartialObjectMetadataList) Marshal() (dAtA []byte, err error) { 142 | size := m.Size() 143 | dAtA = make([]byte, size) 144 | n, err := m.MarshalTo(dAtA) 145 | if err != nil { 146 | return nil, err 147 | } 148 | return dAtA[:n], nil 149 | } 150 | 151 | func (m *PartialObjectMetadataList) MarshalTo(dAtA []byte) (int, error) { 152 | var i int 153 | _ = i 154 | var l int 155 | _ = l 156 | if len(m.Items) > 0 { 157 | for _, msg := range m.Items { 158 | dAtA[i] = 0xa 159 | i++ 160 | i = encodeVarintGenerated(dAtA, i, uint64(msg.Size())) 161 | n, err := msg.MarshalTo(dAtA[i:]) 162 | if err != nil { 163 | return 0, err 164 | } 165 | i += n 166 | } 167 | } 168 | if m.XXX_unrecognized != nil { 169 | i += copy(dAtA[i:], m.XXX_unrecognized) 170 | } 171 | return i, nil 172 | } 173 | 174 | func (m *TableOptions) Marshal() (dAtA []byte, err error) { 175 | size := m.Size() 176 | dAtA = make([]byte, size) 177 | n, err := m.MarshalTo(dAtA) 178 | if err != nil { 179 | return nil, err 180 | } 181 | return dAtA[:n], nil 182 | } 183 | 184 | func (m *TableOptions) MarshalTo(dAtA []byte) (int, error) { 185 | var i int 186 | _ = i 187 | var l int 188 | _ = l 189 | if m.IncludeObject != nil { 190 | dAtA[i] = 0xa 191 | i++ 192 | i = encodeVarintGenerated(dAtA, i, uint64(len(*m.IncludeObject))) 193 | i += copy(dAtA[i:], *m.IncludeObject) 194 | } 195 | if m.XXX_unrecognized != nil { 196 | i += copy(dAtA[i:], m.XXX_unrecognized) 197 | } 198 | return i, nil 199 | } 200 | 201 | func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int { 202 | for v >= 1<<7 { 203 | dAtA[offset] = uint8(v&0x7f | 0x80) 204 | v >>= 7 205 | offset++ 206 | } 207 | dAtA[offset] = uint8(v) 208 | return offset + 1 209 | } 210 | func (m *PartialObjectMetadata) Size() (n int) { 211 | var l int 212 | _ = l 213 | if m.Metadata != nil { 214 | l = m.Metadata.Size() 215 | n += 1 + l + sovGenerated(uint64(l)) 216 | } 217 | if m.XXX_unrecognized != nil { 218 | n += len(m.XXX_unrecognized) 219 | } 220 | return n 221 | } 222 | 223 | func (m *PartialObjectMetadataList) Size() (n int) { 224 | var l int 225 | _ = l 226 | if len(m.Items) > 0 { 227 | for _, e := range m.Items { 228 | l = e.Size() 229 | n += 1 + l + sovGenerated(uint64(l)) 230 | } 231 | } 232 | if m.XXX_unrecognized != nil { 233 | n += len(m.XXX_unrecognized) 234 | } 235 | return n 236 | } 237 | 238 | func (m *TableOptions) Size() (n int) { 239 | var l int 240 | _ = l 241 | if m.IncludeObject != nil { 242 | l = len(*m.IncludeObject) 243 | n += 1 + l + sovGenerated(uint64(l)) 244 | } 245 | if m.XXX_unrecognized != nil { 246 | n += len(m.XXX_unrecognized) 247 | } 248 | return n 249 | } 250 | 251 | func sovGenerated(x uint64) (n int) { 252 | for { 253 | n++ 254 | x >>= 7 255 | if x == 0 { 256 | break 257 | } 258 | } 259 | return n 260 | } 261 | func sozGenerated(x uint64) (n int) { 262 | return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63)))) 263 | } 264 | func (m *PartialObjectMetadata) Unmarshal(dAtA []byte) error { 265 | l := len(dAtA) 266 | iNdEx := 0 267 | for iNdEx < l { 268 | preIndex := iNdEx 269 | var wire uint64 270 | for shift := uint(0); ; shift += 7 { 271 | if shift >= 64 { 272 | return ErrIntOverflowGenerated 273 | } 274 | if iNdEx >= l { 275 | return io.ErrUnexpectedEOF 276 | } 277 | b := dAtA[iNdEx] 278 | iNdEx++ 279 | wire |= (uint64(b) & 0x7F) << shift 280 | if b < 0x80 { 281 | break 282 | } 283 | } 284 | fieldNum := int32(wire >> 3) 285 | wireType := int(wire & 0x7) 286 | if wireType == 4 { 287 | return fmt.Errorf("proto: PartialObjectMetadata: wiretype end group for non-group") 288 | } 289 | if fieldNum <= 0 { 290 | return fmt.Errorf("proto: PartialObjectMetadata: illegal tag %d (wire type %d)", fieldNum, wire) 291 | } 292 | switch fieldNum { 293 | case 1: 294 | if wireType != 2 { 295 | return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) 296 | } 297 | var msglen int 298 | for shift := uint(0); ; shift += 7 { 299 | if shift >= 64 { 300 | return ErrIntOverflowGenerated 301 | } 302 | if iNdEx >= l { 303 | return io.ErrUnexpectedEOF 304 | } 305 | b := dAtA[iNdEx] 306 | iNdEx++ 307 | msglen |= (int(b) & 0x7F) << shift 308 | if b < 0x80 { 309 | break 310 | } 311 | } 312 | if msglen < 0 { 313 | return ErrInvalidLengthGenerated 314 | } 315 | postIndex := iNdEx + msglen 316 | if postIndex > l { 317 | return io.ErrUnexpectedEOF 318 | } 319 | if m.Metadata == nil { 320 | m.Metadata = &k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta{} 321 | } 322 | if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { 323 | return err 324 | } 325 | iNdEx = postIndex 326 | default: 327 | iNdEx = preIndex 328 | skippy, err := skipGenerated(dAtA[iNdEx:]) 329 | if err != nil { 330 | return err 331 | } 332 | if skippy < 0 { 333 | return ErrInvalidLengthGenerated 334 | } 335 | if (iNdEx + skippy) > l { 336 | return io.ErrUnexpectedEOF 337 | } 338 | m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) 339 | iNdEx += skippy 340 | } 341 | } 342 | 343 | if iNdEx > l { 344 | return io.ErrUnexpectedEOF 345 | } 346 | return nil 347 | } 348 | func (m *PartialObjectMetadataList) Unmarshal(dAtA []byte) error { 349 | l := len(dAtA) 350 | iNdEx := 0 351 | for iNdEx < l { 352 | preIndex := iNdEx 353 | var wire uint64 354 | for shift := uint(0); ; shift += 7 { 355 | if shift >= 64 { 356 | return ErrIntOverflowGenerated 357 | } 358 | if iNdEx >= l { 359 | return io.ErrUnexpectedEOF 360 | } 361 | b := dAtA[iNdEx] 362 | iNdEx++ 363 | wire |= (uint64(b) & 0x7F) << shift 364 | if b < 0x80 { 365 | break 366 | } 367 | } 368 | fieldNum := int32(wire >> 3) 369 | wireType := int(wire & 0x7) 370 | if wireType == 4 { 371 | return fmt.Errorf("proto: PartialObjectMetadataList: wiretype end group for non-group") 372 | } 373 | if fieldNum <= 0 { 374 | return fmt.Errorf("proto: PartialObjectMetadataList: illegal tag %d (wire type %d)", fieldNum, wire) 375 | } 376 | switch fieldNum { 377 | case 1: 378 | if wireType != 2 { 379 | return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) 380 | } 381 | var msglen int 382 | for shift := uint(0); ; shift += 7 { 383 | if shift >= 64 { 384 | return ErrIntOverflowGenerated 385 | } 386 | if iNdEx >= l { 387 | return io.ErrUnexpectedEOF 388 | } 389 | b := dAtA[iNdEx] 390 | iNdEx++ 391 | msglen |= (int(b) & 0x7F) << shift 392 | if b < 0x80 { 393 | break 394 | } 395 | } 396 | if msglen < 0 { 397 | return ErrInvalidLengthGenerated 398 | } 399 | postIndex := iNdEx + msglen 400 | if postIndex > l { 401 | return io.ErrUnexpectedEOF 402 | } 403 | m.Items = append(m.Items, &PartialObjectMetadata{}) 404 | if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { 405 | return err 406 | } 407 | iNdEx = postIndex 408 | default: 409 | iNdEx = preIndex 410 | skippy, err := skipGenerated(dAtA[iNdEx:]) 411 | if err != nil { 412 | return err 413 | } 414 | if skippy < 0 { 415 | return ErrInvalidLengthGenerated 416 | } 417 | if (iNdEx + skippy) > l { 418 | return io.ErrUnexpectedEOF 419 | } 420 | m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) 421 | iNdEx += skippy 422 | } 423 | } 424 | 425 | if iNdEx > l { 426 | return io.ErrUnexpectedEOF 427 | } 428 | return nil 429 | } 430 | func (m *TableOptions) Unmarshal(dAtA []byte) error { 431 | l := len(dAtA) 432 | iNdEx := 0 433 | for iNdEx < l { 434 | preIndex := iNdEx 435 | var wire uint64 436 | for shift := uint(0); ; shift += 7 { 437 | if shift >= 64 { 438 | return ErrIntOverflowGenerated 439 | } 440 | if iNdEx >= l { 441 | return io.ErrUnexpectedEOF 442 | } 443 | b := dAtA[iNdEx] 444 | iNdEx++ 445 | wire |= (uint64(b) & 0x7F) << shift 446 | if b < 0x80 { 447 | break 448 | } 449 | } 450 | fieldNum := int32(wire >> 3) 451 | wireType := int(wire & 0x7) 452 | if wireType == 4 { 453 | return fmt.Errorf("proto: TableOptions: wiretype end group for non-group") 454 | } 455 | if fieldNum <= 0 { 456 | return fmt.Errorf("proto: TableOptions: illegal tag %d (wire type %d)", fieldNum, wire) 457 | } 458 | switch fieldNum { 459 | case 1: 460 | if wireType != 2 { 461 | return fmt.Errorf("proto: wrong wireType = %d for field IncludeObject", wireType) 462 | } 463 | var stringLen uint64 464 | for shift := uint(0); ; shift += 7 { 465 | if shift >= 64 { 466 | return ErrIntOverflowGenerated 467 | } 468 | if iNdEx >= l { 469 | return io.ErrUnexpectedEOF 470 | } 471 | b := dAtA[iNdEx] 472 | iNdEx++ 473 | stringLen |= (uint64(b) & 0x7F) << shift 474 | if b < 0x80 { 475 | break 476 | } 477 | } 478 | intStringLen := int(stringLen) 479 | if intStringLen < 0 { 480 | return ErrInvalidLengthGenerated 481 | } 482 | postIndex := iNdEx + intStringLen 483 | if postIndex > l { 484 | return io.ErrUnexpectedEOF 485 | } 486 | s := string(dAtA[iNdEx:postIndex]) 487 | m.IncludeObject = &s 488 | iNdEx = postIndex 489 | default: 490 | iNdEx = preIndex 491 | skippy, err := skipGenerated(dAtA[iNdEx:]) 492 | if err != nil { 493 | return err 494 | } 495 | if skippy < 0 { 496 | return ErrInvalidLengthGenerated 497 | } 498 | if (iNdEx + skippy) > l { 499 | return io.ErrUnexpectedEOF 500 | } 501 | m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) 502 | iNdEx += skippy 503 | } 504 | } 505 | 506 | if iNdEx > l { 507 | return io.ErrUnexpectedEOF 508 | } 509 | return nil 510 | } 511 | func skipGenerated(dAtA []byte) (n int, err error) { 512 | l := len(dAtA) 513 | iNdEx := 0 514 | for iNdEx < l { 515 | var wire uint64 516 | for shift := uint(0); ; shift += 7 { 517 | if shift >= 64 { 518 | return 0, ErrIntOverflowGenerated 519 | } 520 | if iNdEx >= l { 521 | return 0, io.ErrUnexpectedEOF 522 | } 523 | b := dAtA[iNdEx] 524 | iNdEx++ 525 | wire |= (uint64(b) & 0x7F) << shift 526 | if b < 0x80 { 527 | break 528 | } 529 | } 530 | wireType := int(wire & 0x7) 531 | switch wireType { 532 | case 0: 533 | for shift := uint(0); ; shift += 7 { 534 | if shift >= 64 { 535 | return 0, ErrIntOverflowGenerated 536 | } 537 | if iNdEx >= l { 538 | return 0, io.ErrUnexpectedEOF 539 | } 540 | iNdEx++ 541 | if dAtA[iNdEx-1] < 0x80 { 542 | break 543 | } 544 | } 545 | return iNdEx, nil 546 | case 1: 547 | iNdEx += 8 548 | return iNdEx, nil 549 | case 2: 550 | var length int 551 | for shift := uint(0); ; shift += 7 { 552 | if shift >= 64 { 553 | return 0, ErrIntOverflowGenerated 554 | } 555 | if iNdEx >= l { 556 | return 0, io.ErrUnexpectedEOF 557 | } 558 | b := dAtA[iNdEx] 559 | iNdEx++ 560 | length |= (int(b) & 0x7F) << shift 561 | if b < 0x80 { 562 | break 563 | } 564 | } 565 | iNdEx += length 566 | if length < 0 { 567 | return 0, ErrInvalidLengthGenerated 568 | } 569 | return iNdEx, nil 570 | case 3: 571 | for { 572 | var innerWire uint64 573 | var start int = iNdEx 574 | for shift := uint(0); ; shift += 7 { 575 | if shift >= 64 { 576 | return 0, ErrIntOverflowGenerated 577 | } 578 | if iNdEx >= l { 579 | return 0, io.ErrUnexpectedEOF 580 | } 581 | b := dAtA[iNdEx] 582 | iNdEx++ 583 | innerWire |= (uint64(b) & 0x7F) << shift 584 | if b < 0x80 { 585 | break 586 | } 587 | } 588 | innerWireType := int(innerWire & 0x7) 589 | if innerWireType == 4 { 590 | break 591 | } 592 | next, err := skipGenerated(dAtA[start:]) 593 | if err != nil { 594 | return 0, err 595 | } 596 | iNdEx = start + next 597 | } 598 | return iNdEx, nil 599 | case 4: 600 | return iNdEx, nil 601 | case 5: 602 | iNdEx += 4 603 | return iNdEx, nil 604 | default: 605 | return 0, fmt.Errorf("proto: illegal wireType %d", wireType) 606 | } 607 | } 608 | panic("unreachable") 609 | } 610 | 611 | var ( 612 | ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling") 613 | ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow") 614 | ) 615 | 616 | func init() { 617 | proto.RegisterFile("k8s.io/apimachinery/pkg/apis/meta/v1beta1/generated.proto", fileDescriptorGenerated) 618 | } 619 | 620 | var fileDescriptorGenerated = []byte{ 621 | // 278 bytes of a gzipped FileDescriptorProto 622 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x90, 0xcf, 0x4a, 0xf3, 0x40, 623 | 0x14, 0xc5, 0xbf, 0xe1, 0x43, 0xd4, 0xa9, 0x6e, 0x02, 0x42, 0xdb, 0x45, 0x28, 0xc1, 0x45, 0xdd, 624 | 0xdc, 0xb1, 0xa5, 0x88, 0xee, 0xc4, 0x75, 0xa5, 0x12, 0xc4, 0x85, 0xbb, 0x9b, 0xe4, 0x92, 0x8e, 625 | 0xc9, 0x24, 0x21, 0x73, 0x2b, 0xf8, 0x26, 0x3e, 0x92, 0x4b, 0x1f, 0x41, 0xe2, 0x8b, 0x48, 0x4c, 626 | 0xf0, 0x4f, 0xad, 0x98, 0xdd, 0x70, 0x98, 0xdf, 0xf9, 0x71, 0xae, 0x3c, 0x4b, 0x4e, 0x2d, 0xe8, 627 | 0x5c, 0x61, 0xa1, 0x0d, 0x86, 0x4b, 0x9d, 0x51, 0xf9, 0xa0, 0x8a, 0x24, 0xae, 0x03, 0xab, 0x0c, 628 | 0x31, 0xaa, 0xfb, 0x49, 0x40, 0x8c, 0x13, 0x15, 0x53, 0x46, 0x25, 0x32, 0x45, 0x50, 0x94, 0x39, 629 | 0xe7, 0xce, 0x51, 0x83, 0xc2, 0x57, 0x14, 0x8a, 0x24, 0xae, 0x03, 0x0b, 0x35, 0x0a, 0x2d, 0x3a, 630 | 0x9c, 0x75, 0xb1, 0xac, 0x0b, 0x86, 0xea, 0x37, 0xaa, 0x5c, 0x65, 0xac, 0x0d, 0xfd, 0x00, 0x4e, 631 | 0xfe, 0x02, 0x6c, 0xb8, 0x24, 0x83, 0xeb, 0x9c, 0x47, 0xf2, 0xe0, 0x0a, 0x4b, 0xd6, 0x98, 0x2e, 632 | 0x82, 0x3b, 0x0a, 0xf9, 0x92, 0x18, 0x23, 0x64, 0x74, 0xe6, 0x72, 0xc7, 0xb4, 0xef, 0xbe, 0x18, 633 | 0x89, 0x71, 0x6f, 0x7a, 0x0c, 0x5d, 0x56, 0xc3, 0x67, 0x8f, 0xff, 0xd1, 0xe0, 0x59, 0x39, 0xd8, 634 | 0xa8, 0x99, 0x6b, 0xcb, 0xce, 0x8d, 0xdc, 0xd2, 0x4c, 0xc6, 0xf6, 0xc5, 0xe8, 0xff, 0xb8, 0x37, 635 | 0x3d, 0x87, 0xce, 0xd7, 0x85, 0x8d, 0xa5, 0x7e, 0x53, 0xe7, 0xcd, 0xe4, 0xde, 0x35, 0x06, 0x29, 636 | 0x2d, 0x0a, 0xd6, 0x79, 0x66, 0x9d, 0x43, 0xb9, 0xaf, 0xb3, 0x30, 0x5d, 0x45, 0xd4, 0xfc, 0x7f, 637 | 0xdf, 0xb5, 0xeb, 0x7f, 0x0f, 0x2f, 0x06, 0x4f, 0x95, 0x2b, 0x9e, 0x2b, 0x57, 0xbc, 0x54, 0xae, 638 | 0x78, 0x7c, 0x75, 0xff, 0xdd, 0x6e, 0xb7, 0xb6, 0xb7, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa6, 639 | 0x40, 0x78, 0x32, 0x02, 0x00, 0x00, 640 | } 641 | -------------------------------------------------------------------------------- /apis/networking/v1/register.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("networking.k8s.io", "v1", "networkpolicies", true, &NetworkPolicy{}) 7 | 8 | k8s.RegisterList("networking.k8s.io", "v1", "networkpolicies", true, &NetworkPolicyList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/policy/v1beta1/register.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("policy", "v1beta1", "poddisruptionbudgets", true, &PodDisruptionBudget{}) 7 | k8s.Register("policy", "v1beta1", "podsecuritypolicies", false, &PodSecurityPolicy{}) 8 | 9 | k8s.RegisterList("policy", "v1beta1", "poddisruptionbudgets", true, &PodDisruptionBudgetList{}) 10 | k8s.RegisterList("policy", "v1beta1", "podsecuritypolicies", false, &PodSecurityPolicyList{}) 11 | } 12 | -------------------------------------------------------------------------------- /apis/rbac/v1/register.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("rbac.authorization.k8s.io", "v1", "clusterroles", false, &ClusterRole{}) 7 | k8s.Register("rbac.authorization.k8s.io", "v1", "clusterrolebindings", false, &ClusterRoleBinding{}) 8 | k8s.Register("rbac.authorization.k8s.io", "v1", "roles", true, &Role{}) 9 | k8s.Register("rbac.authorization.k8s.io", "v1", "rolebindings", true, &RoleBinding{}) 10 | 11 | k8s.RegisterList("rbac.authorization.k8s.io", "v1", "clusterroles", false, &ClusterRoleList{}) 12 | k8s.RegisterList("rbac.authorization.k8s.io", "v1", "clusterrolebindings", false, &ClusterRoleBindingList{}) 13 | k8s.RegisterList("rbac.authorization.k8s.io", "v1", "roles", true, &RoleList{}) 14 | k8s.RegisterList("rbac.authorization.k8s.io", "v1", "rolebindings", true, &RoleBindingList{}) 15 | } 16 | -------------------------------------------------------------------------------- /apis/rbac/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("rbac.authorization.k8s.io", "v1alpha1", "clusterroles", false, &ClusterRole{}) 7 | k8s.Register("rbac.authorization.k8s.io", "v1alpha1", "clusterrolebindings", false, &ClusterRoleBinding{}) 8 | k8s.Register("rbac.authorization.k8s.io", "v1alpha1", "roles", true, &Role{}) 9 | k8s.Register("rbac.authorization.k8s.io", "v1alpha1", "rolebindings", true, &RoleBinding{}) 10 | 11 | k8s.RegisterList("rbac.authorization.k8s.io", "v1alpha1", "clusterroles", false, &ClusterRoleList{}) 12 | k8s.RegisterList("rbac.authorization.k8s.io", "v1alpha1", "clusterrolebindings", false, &ClusterRoleBindingList{}) 13 | k8s.RegisterList("rbac.authorization.k8s.io", "v1alpha1", "roles", true, &RoleList{}) 14 | k8s.RegisterList("rbac.authorization.k8s.io", "v1alpha1", "rolebindings", true, &RoleBindingList{}) 15 | } 16 | -------------------------------------------------------------------------------- /apis/rbac/v1beta1/register.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("rbac.authorization.k8s.io", "v1beta1", "clusterroles", false, &ClusterRole{}) 7 | k8s.Register("rbac.authorization.k8s.io", "v1beta1", "clusterrolebindings", false, &ClusterRoleBinding{}) 8 | k8s.Register("rbac.authorization.k8s.io", "v1beta1", "roles", true, &Role{}) 9 | k8s.Register("rbac.authorization.k8s.io", "v1beta1", "rolebindings", true, &RoleBinding{}) 10 | 11 | k8s.RegisterList("rbac.authorization.k8s.io", "v1beta1", "clusterroles", false, &ClusterRoleList{}) 12 | k8s.RegisterList("rbac.authorization.k8s.io", "v1beta1", "clusterrolebindings", false, &ClusterRoleBindingList{}) 13 | k8s.RegisterList("rbac.authorization.k8s.io", "v1beta1", "roles", true, &RoleList{}) 14 | k8s.RegisterList("rbac.authorization.k8s.io", "v1beta1", "rolebindings", true, &RoleBindingList{}) 15 | } 16 | -------------------------------------------------------------------------------- /apis/resource/generated.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: k8s.io/apimachinery/pkg/api/resource/generated.proto 3 | 4 | /* 5 | Package resource is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | k8s.io/apimachinery/pkg/api/resource/generated.proto 9 | 10 | It has these top-level messages: 11 | Quantity 12 | */ 13 | package resource 14 | 15 | import proto "github.com/golang/protobuf/proto" 16 | import fmt "fmt" 17 | import math "math" 18 | 19 | import io "io" 20 | 21 | // Reference imports to suppress errors if they are not otherwise used. 22 | var _ = proto.Marshal 23 | var _ = fmt.Errorf 24 | var _ = math.Inf 25 | 26 | // This is a compile-time assertion to ensure that this generated file 27 | // is compatible with the proto package it is being compiled against. 28 | // A compilation error at this line likely means your copy of the 29 | // proto package needs to be updated. 30 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 31 | 32 | // Quantity is a fixed-point representation of a number. 33 | // It provides convenient marshaling/unmarshaling in JSON and YAML, 34 | // in addition to String() and Int64() accessors. 35 | // 36 | // The serialization format is: 37 | // 38 | // ::= 39 | // (Note that may be empty, from the "" case in .) 40 | // ::= 0 | 1 | ... | 9 41 | // ::= | 42 | // ::= | . | . | . 43 | // ::= "+" | "-" 44 | // ::= | 45 | // ::= | | 46 | // ::= Ki | Mi | Gi | Ti | Pi | Ei 47 | // (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html) 48 | // ::= m | "" | k | M | G | T | P | E 49 | // (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.) 50 | // ::= "e" | "E" 51 | // 52 | // No matter which of the three exponent forms is used, no quantity may represent 53 | // a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal 54 | // places. Numbers larger or more precise will be capped or rounded up. 55 | // (E.g.: 0.1m will rounded up to 1m.) 56 | // This may be extended in the future if we require larger or smaller quantities. 57 | // 58 | // When a Quantity is parsed from a string, it will remember the type of suffix 59 | // it had, and will use the same type again when it is serialized. 60 | // 61 | // Before serializing, Quantity will be put in "canonical form". 62 | // This means that Exponent/suffix will be adjusted up or down (with a 63 | // corresponding increase or decrease in Mantissa) such that: 64 | // a. No precision is lost 65 | // b. No fractional digits will be emitted 66 | // c. The exponent (or suffix) is as large as possible. 67 | // The sign will be omitted unless the number is negative. 68 | // 69 | // Examples: 70 | // 1.5 will be serialized as "1500m" 71 | // 1.5Gi will be serialized as "1536Mi" 72 | // 73 | // Note that the quantity will NEVER be internally represented by a 74 | // floating point number. That is the whole point of this exercise. 75 | // 76 | // Non-canonical values will still parse as long as they are well formed, 77 | // but will be re-emitted in their canonical form. (So always use canonical 78 | // form, or don't diff.) 79 | // 80 | // This format is intended to make it difficult to use these numbers without 81 | // writing some sort of special handling code in the hopes that that will 82 | // cause implementors to also use a fixed point implementation. 83 | // 84 | // +protobuf=true 85 | // +protobuf.embed=string 86 | // +protobuf.options.marshal=false 87 | // +protobuf.options.(gogoproto.goproto_stringer)=false 88 | // +k8s:deepcopy-gen=true 89 | // +k8s:openapi-gen=true 90 | type Quantity struct { 91 | String_ *string `protobuf:"bytes,1,opt,name=string" json:"string,omitempty"` 92 | XXX_unrecognized []byte `json:"-"` 93 | } 94 | 95 | func (m *Quantity) Reset() { *m = Quantity{} } 96 | func (m *Quantity) String() string { return proto.CompactTextString(m) } 97 | func (*Quantity) ProtoMessage() {} 98 | func (*Quantity) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} } 99 | 100 | func (m *Quantity) GetString_() string { 101 | if m != nil && m.String_ != nil { 102 | return *m.String_ 103 | } 104 | return "" 105 | } 106 | 107 | func init() { 108 | proto.RegisterType((*Quantity)(nil), "k8s.io.apimachinery.pkg.api.resource.Quantity") 109 | } 110 | func (m *Quantity) Marshal() (dAtA []byte, err error) { 111 | size := m.Size() 112 | dAtA = make([]byte, size) 113 | n, err := m.MarshalTo(dAtA) 114 | if err != nil { 115 | return nil, err 116 | } 117 | return dAtA[:n], nil 118 | } 119 | 120 | func (m *Quantity) MarshalTo(dAtA []byte) (int, error) { 121 | var i int 122 | _ = i 123 | var l int 124 | _ = l 125 | if m.String_ != nil { 126 | dAtA[i] = 0xa 127 | i++ 128 | i = encodeVarintGenerated(dAtA, i, uint64(len(*m.String_))) 129 | i += copy(dAtA[i:], *m.String_) 130 | } 131 | if m.XXX_unrecognized != nil { 132 | i += copy(dAtA[i:], m.XXX_unrecognized) 133 | } 134 | return i, nil 135 | } 136 | 137 | func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int { 138 | for v >= 1<<7 { 139 | dAtA[offset] = uint8(v&0x7f | 0x80) 140 | v >>= 7 141 | offset++ 142 | } 143 | dAtA[offset] = uint8(v) 144 | return offset + 1 145 | } 146 | func (m *Quantity) Size() (n int) { 147 | var l int 148 | _ = l 149 | if m.String_ != nil { 150 | l = len(*m.String_) 151 | n += 1 + l + sovGenerated(uint64(l)) 152 | } 153 | if m.XXX_unrecognized != nil { 154 | n += len(m.XXX_unrecognized) 155 | } 156 | return n 157 | } 158 | 159 | func sovGenerated(x uint64) (n int) { 160 | for { 161 | n++ 162 | x >>= 7 163 | if x == 0 { 164 | break 165 | } 166 | } 167 | return n 168 | } 169 | func sozGenerated(x uint64) (n int) { 170 | return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63)))) 171 | } 172 | func (m *Quantity) Unmarshal(dAtA []byte) error { 173 | l := len(dAtA) 174 | iNdEx := 0 175 | for iNdEx < l { 176 | preIndex := iNdEx 177 | var wire uint64 178 | for shift := uint(0); ; shift += 7 { 179 | if shift >= 64 { 180 | return ErrIntOverflowGenerated 181 | } 182 | if iNdEx >= l { 183 | return io.ErrUnexpectedEOF 184 | } 185 | b := dAtA[iNdEx] 186 | iNdEx++ 187 | wire |= (uint64(b) & 0x7F) << shift 188 | if b < 0x80 { 189 | break 190 | } 191 | } 192 | fieldNum := int32(wire >> 3) 193 | wireType := int(wire & 0x7) 194 | if wireType == 4 { 195 | return fmt.Errorf("proto: Quantity: wiretype end group for non-group") 196 | } 197 | if fieldNum <= 0 { 198 | return fmt.Errorf("proto: Quantity: illegal tag %d (wire type %d)", fieldNum, wire) 199 | } 200 | switch fieldNum { 201 | case 1: 202 | if wireType != 2 { 203 | return fmt.Errorf("proto: wrong wireType = %d for field String_", wireType) 204 | } 205 | var stringLen uint64 206 | for shift := uint(0); ; shift += 7 { 207 | if shift >= 64 { 208 | return ErrIntOverflowGenerated 209 | } 210 | if iNdEx >= l { 211 | return io.ErrUnexpectedEOF 212 | } 213 | b := dAtA[iNdEx] 214 | iNdEx++ 215 | stringLen |= (uint64(b) & 0x7F) << shift 216 | if b < 0x80 { 217 | break 218 | } 219 | } 220 | intStringLen := int(stringLen) 221 | if intStringLen < 0 { 222 | return ErrInvalidLengthGenerated 223 | } 224 | postIndex := iNdEx + intStringLen 225 | if postIndex > l { 226 | return io.ErrUnexpectedEOF 227 | } 228 | s := string(dAtA[iNdEx:postIndex]) 229 | m.String_ = &s 230 | iNdEx = postIndex 231 | default: 232 | iNdEx = preIndex 233 | skippy, err := skipGenerated(dAtA[iNdEx:]) 234 | if err != nil { 235 | return err 236 | } 237 | if skippy < 0 { 238 | return ErrInvalidLengthGenerated 239 | } 240 | if (iNdEx + skippy) > l { 241 | return io.ErrUnexpectedEOF 242 | } 243 | m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) 244 | iNdEx += skippy 245 | } 246 | } 247 | 248 | if iNdEx > l { 249 | return io.ErrUnexpectedEOF 250 | } 251 | return nil 252 | } 253 | func skipGenerated(dAtA []byte) (n int, err error) { 254 | l := len(dAtA) 255 | iNdEx := 0 256 | for iNdEx < l { 257 | var wire uint64 258 | for shift := uint(0); ; shift += 7 { 259 | if shift >= 64 { 260 | return 0, ErrIntOverflowGenerated 261 | } 262 | if iNdEx >= l { 263 | return 0, io.ErrUnexpectedEOF 264 | } 265 | b := dAtA[iNdEx] 266 | iNdEx++ 267 | wire |= (uint64(b) & 0x7F) << shift 268 | if b < 0x80 { 269 | break 270 | } 271 | } 272 | wireType := int(wire & 0x7) 273 | switch wireType { 274 | case 0: 275 | for shift := uint(0); ; shift += 7 { 276 | if shift >= 64 { 277 | return 0, ErrIntOverflowGenerated 278 | } 279 | if iNdEx >= l { 280 | return 0, io.ErrUnexpectedEOF 281 | } 282 | iNdEx++ 283 | if dAtA[iNdEx-1] < 0x80 { 284 | break 285 | } 286 | } 287 | return iNdEx, nil 288 | case 1: 289 | iNdEx += 8 290 | return iNdEx, nil 291 | case 2: 292 | var length int 293 | for shift := uint(0); ; shift += 7 { 294 | if shift >= 64 { 295 | return 0, ErrIntOverflowGenerated 296 | } 297 | if iNdEx >= l { 298 | return 0, io.ErrUnexpectedEOF 299 | } 300 | b := dAtA[iNdEx] 301 | iNdEx++ 302 | length |= (int(b) & 0x7F) << shift 303 | if b < 0x80 { 304 | break 305 | } 306 | } 307 | iNdEx += length 308 | if length < 0 { 309 | return 0, ErrInvalidLengthGenerated 310 | } 311 | return iNdEx, nil 312 | case 3: 313 | for { 314 | var innerWire uint64 315 | var start int = iNdEx 316 | for shift := uint(0); ; shift += 7 { 317 | if shift >= 64 { 318 | return 0, ErrIntOverflowGenerated 319 | } 320 | if iNdEx >= l { 321 | return 0, io.ErrUnexpectedEOF 322 | } 323 | b := dAtA[iNdEx] 324 | iNdEx++ 325 | innerWire |= (uint64(b) & 0x7F) << shift 326 | if b < 0x80 { 327 | break 328 | } 329 | } 330 | innerWireType := int(innerWire & 0x7) 331 | if innerWireType == 4 { 332 | break 333 | } 334 | next, err := skipGenerated(dAtA[start:]) 335 | if err != nil { 336 | return 0, err 337 | } 338 | iNdEx = start + next 339 | } 340 | return iNdEx, nil 341 | case 4: 342 | return iNdEx, nil 343 | case 5: 344 | iNdEx += 4 345 | return iNdEx, nil 346 | default: 347 | return 0, fmt.Errorf("proto: illegal wireType %d", wireType) 348 | } 349 | } 350 | panic("unreachable") 351 | } 352 | 353 | var ( 354 | ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling") 355 | ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow") 356 | ) 357 | 358 | func init() { 359 | proto.RegisterFile("k8s.io/apimachinery/pkg/api/resource/generated.proto", fileDescriptorGenerated) 360 | } 361 | 362 | var fileDescriptorGenerated = []byte{ 363 | // 146 bytes of a gzipped FileDescriptorProto 364 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0xc9, 0xb6, 0x28, 0xd6, 365 | 0xcb, 0xcc, 0xd7, 0x4f, 0x2c, 0xc8, 0xcc, 0x4d, 0x4c, 0xce, 0xc8, 0xcc, 0x4b, 0x2d, 0xaa, 0xd4, 366 | 0x2f, 0xc8, 0x4e, 0x07, 0x09, 0xe8, 0x17, 0xa5, 0x16, 0xe7, 0x97, 0x16, 0x25, 0xa7, 0xea, 0xa7, 367 | 0xa7, 0xe6, 0xa5, 0x16, 0x25, 0x96, 0xa4, 0xa6, 0xe8, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0xa9, 368 | 0x40, 0x74, 0xe9, 0x21, 0xeb, 0xd2, 0x2b, 0xc8, 0x4e, 0x07, 0x09, 0xe8, 0xc1, 0x74, 0x29, 0x29, 369 | 0x71, 0x71, 0x04, 0x96, 0x26, 0xe6, 0x95, 0x64, 0x96, 0x54, 0x0a, 0x89, 0x71, 0xb1, 0x15, 0x97, 370 | 0x14, 0x65, 0xe6, 0xa5, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0x41, 0x79, 0x4e, 0x52, 0x27, 371 | 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x8c, 0xc7, 0x72, 0x0c, 372 | 0x51, 0x1c, 0x30, 0xfd, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x78, 0xb7, 0x88, 0x6c, 0x9c, 0x00, 373 | 0x00, 0x00, 374 | } 375 | -------------------------------------------------------------------------------- /apis/scheduling/v1alpha1/generated.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: k8s.io/api/scheduling/v1alpha1/generated.proto 3 | 4 | /* 5 | Package v1alpha1 is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | k8s.io/api/scheduling/v1alpha1/generated.proto 9 | 10 | It has these top-level messages: 11 | PriorityClass 12 | PriorityClassList 13 | */ 14 | package v1alpha1 15 | 16 | import proto "github.com/golang/protobuf/proto" 17 | import fmt "fmt" 18 | import math "math" 19 | import k8s_io_apimachinery_pkg_apis_meta_v1 "github.com/ericchiang/k8s/apis/meta/v1" 20 | import _ "github.com/ericchiang/k8s/runtime" 21 | import _ "github.com/ericchiang/k8s/runtime/schema" 22 | 23 | import io "io" 24 | 25 | // Reference imports to suppress errors if they are not otherwise used. 26 | var _ = proto.Marshal 27 | var _ = fmt.Errorf 28 | var _ = math.Inf 29 | 30 | // This is a compile-time assertion to ensure that this generated file 31 | // is compatible with the proto package it is being compiled against. 32 | // A compilation error at this line likely means your copy of the 33 | // proto package needs to be updated. 34 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 35 | 36 | // PriorityClass defines mapping from a priority class name to the priority 37 | // integer value. The value can be any valid integer. 38 | type PriorityClass struct { 39 | // Standard object's metadata. 40 | // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata 41 | // +optional 42 | Metadata *k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta `protobuf:"bytes,1,opt,name=metadata" json:"metadata,omitempty"` 43 | // The value of this priority class. This is the actual priority that pods 44 | // receive when they have the name of this class in their pod spec. 45 | Value *int32 `protobuf:"varint,2,opt,name=value" json:"value,omitempty"` 46 | // globalDefault specifies whether this PriorityClass should be considered as 47 | // the default priority for pods that do not have any priority class. 48 | // Only one PriorityClass can be marked as `globalDefault`. However, if more than 49 | // one PriorityClasses exists with their `globalDefault` field set to true, 50 | // the smallest value of such global default PriorityClasses will be used as the default priority. 51 | // +optional 52 | GlobalDefault *bool `protobuf:"varint,3,opt,name=globalDefault" json:"globalDefault,omitempty"` 53 | // description is an arbitrary string that usually provides guidelines on 54 | // when this priority class should be used. 55 | // +optional 56 | Description *string `protobuf:"bytes,4,opt,name=description" json:"description,omitempty"` 57 | XXX_unrecognized []byte `json:"-"` 58 | } 59 | 60 | func (m *PriorityClass) Reset() { *m = PriorityClass{} } 61 | func (m *PriorityClass) String() string { return proto.CompactTextString(m) } 62 | func (*PriorityClass) ProtoMessage() {} 63 | func (*PriorityClass) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} } 64 | 65 | func (m *PriorityClass) GetMetadata() *k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta { 66 | if m != nil { 67 | return m.Metadata 68 | } 69 | return nil 70 | } 71 | 72 | func (m *PriorityClass) GetValue() int32 { 73 | if m != nil && m.Value != nil { 74 | return *m.Value 75 | } 76 | return 0 77 | } 78 | 79 | func (m *PriorityClass) GetGlobalDefault() bool { 80 | if m != nil && m.GlobalDefault != nil { 81 | return *m.GlobalDefault 82 | } 83 | return false 84 | } 85 | 86 | func (m *PriorityClass) GetDescription() string { 87 | if m != nil && m.Description != nil { 88 | return *m.Description 89 | } 90 | return "" 91 | } 92 | 93 | // PriorityClassList is a collection of priority classes. 94 | type PriorityClassList struct { 95 | // Standard list metadata 96 | // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata 97 | // +optional 98 | Metadata *k8s_io_apimachinery_pkg_apis_meta_v1.ListMeta `protobuf:"bytes,1,opt,name=metadata" json:"metadata,omitempty"` 99 | // items is the list of PriorityClasses 100 | Items []*PriorityClass `protobuf:"bytes,2,rep,name=items" json:"items,omitempty"` 101 | XXX_unrecognized []byte `json:"-"` 102 | } 103 | 104 | func (m *PriorityClassList) Reset() { *m = PriorityClassList{} } 105 | func (m *PriorityClassList) String() string { return proto.CompactTextString(m) } 106 | func (*PriorityClassList) ProtoMessage() {} 107 | func (*PriorityClassList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{1} } 108 | 109 | func (m *PriorityClassList) GetMetadata() *k8s_io_apimachinery_pkg_apis_meta_v1.ListMeta { 110 | if m != nil { 111 | return m.Metadata 112 | } 113 | return nil 114 | } 115 | 116 | func (m *PriorityClassList) GetItems() []*PriorityClass { 117 | if m != nil { 118 | return m.Items 119 | } 120 | return nil 121 | } 122 | 123 | func init() { 124 | proto.RegisterType((*PriorityClass)(nil), "k8s.io.api.scheduling.v1alpha1.PriorityClass") 125 | proto.RegisterType((*PriorityClassList)(nil), "k8s.io.api.scheduling.v1alpha1.PriorityClassList") 126 | } 127 | func (m *PriorityClass) Marshal() (dAtA []byte, err error) { 128 | size := m.Size() 129 | dAtA = make([]byte, size) 130 | n, err := m.MarshalTo(dAtA) 131 | if err != nil { 132 | return nil, err 133 | } 134 | return dAtA[:n], nil 135 | } 136 | 137 | func (m *PriorityClass) MarshalTo(dAtA []byte) (int, error) { 138 | var i int 139 | _ = i 140 | var l int 141 | _ = l 142 | if m.Metadata != nil { 143 | dAtA[i] = 0xa 144 | i++ 145 | i = encodeVarintGenerated(dAtA, i, uint64(m.Metadata.Size())) 146 | n1, err := m.Metadata.MarshalTo(dAtA[i:]) 147 | if err != nil { 148 | return 0, err 149 | } 150 | i += n1 151 | } 152 | if m.Value != nil { 153 | dAtA[i] = 0x10 154 | i++ 155 | i = encodeVarintGenerated(dAtA, i, uint64(*m.Value)) 156 | } 157 | if m.GlobalDefault != nil { 158 | dAtA[i] = 0x18 159 | i++ 160 | if *m.GlobalDefault { 161 | dAtA[i] = 1 162 | } else { 163 | dAtA[i] = 0 164 | } 165 | i++ 166 | } 167 | if m.Description != nil { 168 | dAtA[i] = 0x22 169 | i++ 170 | i = encodeVarintGenerated(dAtA, i, uint64(len(*m.Description))) 171 | i += copy(dAtA[i:], *m.Description) 172 | } 173 | if m.XXX_unrecognized != nil { 174 | i += copy(dAtA[i:], m.XXX_unrecognized) 175 | } 176 | return i, nil 177 | } 178 | 179 | func (m *PriorityClassList) Marshal() (dAtA []byte, err error) { 180 | size := m.Size() 181 | dAtA = make([]byte, size) 182 | n, err := m.MarshalTo(dAtA) 183 | if err != nil { 184 | return nil, err 185 | } 186 | return dAtA[:n], nil 187 | } 188 | 189 | func (m *PriorityClassList) MarshalTo(dAtA []byte) (int, error) { 190 | var i int 191 | _ = i 192 | var l int 193 | _ = l 194 | if m.Metadata != nil { 195 | dAtA[i] = 0xa 196 | i++ 197 | i = encodeVarintGenerated(dAtA, i, uint64(m.Metadata.Size())) 198 | n2, err := m.Metadata.MarshalTo(dAtA[i:]) 199 | if err != nil { 200 | return 0, err 201 | } 202 | i += n2 203 | } 204 | if len(m.Items) > 0 { 205 | for _, msg := range m.Items { 206 | dAtA[i] = 0x12 207 | i++ 208 | i = encodeVarintGenerated(dAtA, i, uint64(msg.Size())) 209 | n, err := msg.MarshalTo(dAtA[i:]) 210 | if err != nil { 211 | return 0, err 212 | } 213 | i += n 214 | } 215 | } 216 | if m.XXX_unrecognized != nil { 217 | i += copy(dAtA[i:], m.XXX_unrecognized) 218 | } 219 | return i, nil 220 | } 221 | 222 | func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int { 223 | for v >= 1<<7 { 224 | dAtA[offset] = uint8(v&0x7f | 0x80) 225 | v >>= 7 226 | offset++ 227 | } 228 | dAtA[offset] = uint8(v) 229 | return offset + 1 230 | } 231 | func (m *PriorityClass) Size() (n int) { 232 | var l int 233 | _ = l 234 | if m.Metadata != nil { 235 | l = m.Metadata.Size() 236 | n += 1 + l + sovGenerated(uint64(l)) 237 | } 238 | if m.Value != nil { 239 | n += 1 + sovGenerated(uint64(*m.Value)) 240 | } 241 | if m.GlobalDefault != nil { 242 | n += 2 243 | } 244 | if m.Description != nil { 245 | l = len(*m.Description) 246 | n += 1 + l + sovGenerated(uint64(l)) 247 | } 248 | if m.XXX_unrecognized != nil { 249 | n += len(m.XXX_unrecognized) 250 | } 251 | return n 252 | } 253 | 254 | func (m *PriorityClassList) Size() (n int) { 255 | var l int 256 | _ = l 257 | if m.Metadata != nil { 258 | l = m.Metadata.Size() 259 | n += 1 + l + sovGenerated(uint64(l)) 260 | } 261 | if len(m.Items) > 0 { 262 | for _, e := range m.Items { 263 | l = e.Size() 264 | n += 1 + l + sovGenerated(uint64(l)) 265 | } 266 | } 267 | if m.XXX_unrecognized != nil { 268 | n += len(m.XXX_unrecognized) 269 | } 270 | return n 271 | } 272 | 273 | func sovGenerated(x uint64) (n int) { 274 | for { 275 | n++ 276 | x >>= 7 277 | if x == 0 { 278 | break 279 | } 280 | } 281 | return n 282 | } 283 | func sozGenerated(x uint64) (n int) { 284 | return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63)))) 285 | } 286 | func (m *PriorityClass) Unmarshal(dAtA []byte) error { 287 | l := len(dAtA) 288 | iNdEx := 0 289 | for iNdEx < l { 290 | preIndex := iNdEx 291 | var wire uint64 292 | for shift := uint(0); ; shift += 7 { 293 | if shift >= 64 { 294 | return ErrIntOverflowGenerated 295 | } 296 | if iNdEx >= l { 297 | return io.ErrUnexpectedEOF 298 | } 299 | b := dAtA[iNdEx] 300 | iNdEx++ 301 | wire |= (uint64(b) & 0x7F) << shift 302 | if b < 0x80 { 303 | break 304 | } 305 | } 306 | fieldNum := int32(wire >> 3) 307 | wireType := int(wire & 0x7) 308 | if wireType == 4 { 309 | return fmt.Errorf("proto: PriorityClass: wiretype end group for non-group") 310 | } 311 | if fieldNum <= 0 { 312 | return fmt.Errorf("proto: PriorityClass: illegal tag %d (wire type %d)", fieldNum, wire) 313 | } 314 | switch fieldNum { 315 | case 1: 316 | if wireType != 2 { 317 | return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) 318 | } 319 | var msglen int 320 | for shift := uint(0); ; shift += 7 { 321 | if shift >= 64 { 322 | return ErrIntOverflowGenerated 323 | } 324 | if iNdEx >= l { 325 | return io.ErrUnexpectedEOF 326 | } 327 | b := dAtA[iNdEx] 328 | iNdEx++ 329 | msglen |= (int(b) & 0x7F) << shift 330 | if b < 0x80 { 331 | break 332 | } 333 | } 334 | if msglen < 0 { 335 | return ErrInvalidLengthGenerated 336 | } 337 | postIndex := iNdEx + msglen 338 | if postIndex > l { 339 | return io.ErrUnexpectedEOF 340 | } 341 | if m.Metadata == nil { 342 | m.Metadata = &k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta{} 343 | } 344 | if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { 345 | return err 346 | } 347 | iNdEx = postIndex 348 | case 2: 349 | if wireType != 0 { 350 | return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) 351 | } 352 | var v int32 353 | for shift := uint(0); ; shift += 7 { 354 | if shift >= 64 { 355 | return ErrIntOverflowGenerated 356 | } 357 | if iNdEx >= l { 358 | return io.ErrUnexpectedEOF 359 | } 360 | b := dAtA[iNdEx] 361 | iNdEx++ 362 | v |= (int32(b) & 0x7F) << shift 363 | if b < 0x80 { 364 | break 365 | } 366 | } 367 | m.Value = &v 368 | case 3: 369 | if wireType != 0 { 370 | return fmt.Errorf("proto: wrong wireType = %d for field GlobalDefault", wireType) 371 | } 372 | var v int 373 | for shift := uint(0); ; shift += 7 { 374 | if shift >= 64 { 375 | return ErrIntOverflowGenerated 376 | } 377 | if iNdEx >= l { 378 | return io.ErrUnexpectedEOF 379 | } 380 | b := dAtA[iNdEx] 381 | iNdEx++ 382 | v |= (int(b) & 0x7F) << shift 383 | if b < 0x80 { 384 | break 385 | } 386 | } 387 | b := bool(v != 0) 388 | m.GlobalDefault = &b 389 | case 4: 390 | if wireType != 2 { 391 | return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) 392 | } 393 | var stringLen uint64 394 | for shift := uint(0); ; shift += 7 { 395 | if shift >= 64 { 396 | return ErrIntOverflowGenerated 397 | } 398 | if iNdEx >= l { 399 | return io.ErrUnexpectedEOF 400 | } 401 | b := dAtA[iNdEx] 402 | iNdEx++ 403 | stringLen |= (uint64(b) & 0x7F) << shift 404 | if b < 0x80 { 405 | break 406 | } 407 | } 408 | intStringLen := int(stringLen) 409 | if intStringLen < 0 { 410 | return ErrInvalidLengthGenerated 411 | } 412 | postIndex := iNdEx + intStringLen 413 | if postIndex > l { 414 | return io.ErrUnexpectedEOF 415 | } 416 | s := string(dAtA[iNdEx:postIndex]) 417 | m.Description = &s 418 | iNdEx = postIndex 419 | default: 420 | iNdEx = preIndex 421 | skippy, err := skipGenerated(dAtA[iNdEx:]) 422 | if err != nil { 423 | return err 424 | } 425 | if skippy < 0 { 426 | return ErrInvalidLengthGenerated 427 | } 428 | if (iNdEx + skippy) > l { 429 | return io.ErrUnexpectedEOF 430 | } 431 | m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) 432 | iNdEx += skippy 433 | } 434 | } 435 | 436 | if iNdEx > l { 437 | return io.ErrUnexpectedEOF 438 | } 439 | return nil 440 | } 441 | func (m *PriorityClassList) Unmarshal(dAtA []byte) error { 442 | l := len(dAtA) 443 | iNdEx := 0 444 | for iNdEx < l { 445 | preIndex := iNdEx 446 | var wire uint64 447 | for shift := uint(0); ; shift += 7 { 448 | if shift >= 64 { 449 | return ErrIntOverflowGenerated 450 | } 451 | if iNdEx >= l { 452 | return io.ErrUnexpectedEOF 453 | } 454 | b := dAtA[iNdEx] 455 | iNdEx++ 456 | wire |= (uint64(b) & 0x7F) << shift 457 | if b < 0x80 { 458 | break 459 | } 460 | } 461 | fieldNum := int32(wire >> 3) 462 | wireType := int(wire & 0x7) 463 | if wireType == 4 { 464 | return fmt.Errorf("proto: PriorityClassList: wiretype end group for non-group") 465 | } 466 | if fieldNum <= 0 { 467 | return fmt.Errorf("proto: PriorityClassList: illegal tag %d (wire type %d)", fieldNum, wire) 468 | } 469 | switch fieldNum { 470 | case 1: 471 | if wireType != 2 { 472 | return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) 473 | } 474 | var msglen int 475 | for shift := uint(0); ; shift += 7 { 476 | if shift >= 64 { 477 | return ErrIntOverflowGenerated 478 | } 479 | if iNdEx >= l { 480 | return io.ErrUnexpectedEOF 481 | } 482 | b := dAtA[iNdEx] 483 | iNdEx++ 484 | msglen |= (int(b) & 0x7F) << shift 485 | if b < 0x80 { 486 | break 487 | } 488 | } 489 | if msglen < 0 { 490 | return ErrInvalidLengthGenerated 491 | } 492 | postIndex := iNdEx + msglen 493 | if postIndex > l { 494 | return io.ErrUnexpectedEOF 495 | } 496 | if m.Metadata == nil { 497 | m.Metadata = &k8s_io_apimachinery_pkg_apis_meta_v1.ListMeta{} 498 | } 499 | if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { 500 | return err 501 | } 502 | iNdEx = postIndex 503 | case 2: 504 | if wireType != 2 { 505 | return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) 506 | } 507 | var msglen int 508 | for shift := uint(0); ; shift += 7 { 509 | if shift >= 64 { 510 | return ErrIntOverflowGenerated 511 | } 512 | if iNdEx >= l { 513 | return io.ErrUnexpectedEOF 514 | } 515 | b := dAtA[iNdEx] 516 | iNdEx++ 517 | msglen |= (int(b) & 0x7F) << shift 518 | if b < 0x80 { 519 | break 520 | } 521 | } 522 | if msglen < 0 { 523 | return ErrInvalidLengthGenerated 524 | } 525 | postIndex := iNdEx + msglen 526 | if postIndex > l { 527 | return io.ErrUnexpectedEOF 528 | } 529 | m.Items = append(m.Items, &PriorityClass{}) 530 | if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { 531 | return err 532 | } 533 | iNdEx = postIndex 534 | default: 535 | iNdEx = preIndex 536 | skippy, err := skipGenerated(dAtA[iNdEx:]) 537 | if err != nil { 538 | return err 539 | } 540 | if skippy < 0 { 541 | return ErrInvalidLengthGenerated 542 | } 543 | if (iNdEx + skippy) > l { 544 | return io.ErrUnexpectedEOF 545 | } 546 | m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) 547 | iNdEx += skippy 548 | } 549 | } 550 | 551 | if iNdEx > l { 552 | return io.ErrUnexpectedEOF 553 | } 554 | return nil 555 | } 556 | func skipGenerated(dAtA []byte) (n int, err error) { 557 | l := len(dAtA) 558 | iNdEx := 0 559 | for iNdEx < l { 560 | var wire uint64 561 | for shift := uint(0); ; shift += 7 { 562 | if shift >= 64 { 563 | return 0, ErrIntOverflowGenerated 564 | } 565 | if iNdEx >= l { 566 | return 0, io.ErrUnexpectedEOF 567 | } 568 | b := dAtA[iNdEx] 569 | iNdEx++ 570 | wire |= (uint64(b) & 0x7F) << shift 571 | if b < 0x80 { 572 | break 573 | } 574 | } 575 | wireType := int(wire & 0x7) 576 | switch wireType { 577 | case 0: 578 | for shift := uint(0); ; shift += 7 { 579 | if shift >= 64 { 580 | return 0, ErrIntOverflowGenerated 581 | } 582 | if iNdEx >= l { 583 | return 0, io.ErrUnexpectedEOF 584 | } 585 | iNdEx++ 586 | if dAtA[iNdEx-1] < 0x80 { 587 | break 588 | } 589 | } 590 | return iNdEx, nil 591 | case 1: 592 | iNdEx += 8 593 | return iNdEx, nil 594 | case 2: 595 | var length int 596 | for shift := uint(0); ; shift += 7 { 597 | if shift >= 64 { 598 | return 0, ErrIntOverflowGenerated 599 | } 600 | if iNdEx >= l { 601 | return 0, io.ErrUnexpectedEOF 602 | } 603 | b := dAtA[iNdEx] 604 | iNdEx++ 605 | length |= (int(b) & 0x7F) << shift 606 | if b < 0x80 { 607 | break 608 | } 609 | } 610 | iNdEx += length 611 | if length < 0 { 612 | return 0, ErrInvalidLengthGenerated 613 | } 614 | return iNdEx, nil 615 | case 3: 616 | for { 617 | var innerWire uint64 618 | var start int = iNdEx 619 | for shift := uint(0); ; shift += 7 { 620 | if shift >= 64 { 621 | return 0, ErrIntOverflowGenerated 622 | } 623 | if iNdEx >= l { 624 | return 0, io.ErrUnexpectedEOF 625 | } 626 | b := dAtA[iNdEx] 627 | iNdEx++ 628 | innerWire |= (uint64(b) & 0x7F) << shift 629 | if b < 0x80 { 630 | break 631 | } 632 | } 633 | innerWireType := int(innerWire & 0x7) 634 | if innerWireType == 4 { 635 | break 636 | } 637 | next, err := skipGenerated(dAtA[start:]) 638 | if err != nil { 639 | return 0, err 640 | } 641 | iNdEx = start + next 642 | } 643 | return iNdEx, nil 644 | case 4: 645 | return iNdEx, nil 646 | case 5: 647 | iNdEx += 4 648 | return iNdEx, nil 649 | default: 650 | return 0, fmt.Errorf("proto: illegal wireType %d", wireType) 651 | } 652 | } 653 | panic("unreachable") 654 | } 655 | 656 | var ( 657 | ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling") 658 | ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow") 659 | ) 660 | 661 | func init() { 662 | proto.RegisterFile("k8s.io/api/scheduling/v1alpha1/generated.proto", fileDescriptorGenerated) 663 | } 664 | 665 | var fileDescriptorGenerated = []byte{ 666 | // 334 bytes of a gzipped FileDescriptorProto 667 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0xcd, 0x4a, 0xf3, 0x40, 668 | 0x14, 0x86, 0xbf, 0x69, 0xbf, 0x42, 0x9d, 0xd2, 0x85, 0xc1, 0x45, 0xe8, 0x22, 0x84, 0xe2, 0x22, 669 | 0x1b, 0xcf, 0xd8, 0x22, 0xe2, 0xda, 0xba, 0x92, 0x8a, 0x92, 0xa5, 0xbb, 0xd3, 0xe4, 0x98, 0x8e, 670 | 0xcd, 0xcf, 0x30, 0x33, 0x09, 0xf4, 0x4e, 0xbc, 0x00, 0xef, 0xc2, 0x1b, 0x70, 0xe9, 0x25, 0x48, 671 | 0xbd, 0x11, 0x09, 0xa5, 0xad, 0x6d, 0xf1, 0x67, 0x39, 0x2f, 0xef, 0xf3, 0xf2, 0x1c, 0x86, 0xc3, 672 | 0xec, 0xc2, 0x80, 0x2c, 0x04, 0x2a, 0x29, 0x4c, 0x34, 0xa5, 0xb8, 0x4c, 0x65, 0x9e, 0x88, 0x6a, 673 | 0x80, 0xa9, 0x9a, 0xe2, 0x40, 0x24, 0x94, 0x93, 0x46, 0x4b, 0x31, 0x28, 0x5d, 0xd8, 0xc2, 0xf1, 674 | 0x96, 0x7d, 0x40, 0x25, 0x61, 0xd3, 0x87, 0x55, 0xbf, 0x77, 0xb6, 0xd9, 0xcb, 0x30, 0x9a, 0xca, 675 | 0x9c, 0xf4, 0x5c, 0xa8, 0x59, 0x52, 0x07, 0x46, 0x64, 0x64, 0x51, 0x54, 0x7b, 0xab, 0x3d, 0xf1, 676 | 0x1d, 0xa5, 0xcb, 0xdc, 0xca, 0x8c, 0xf6, 0x80, 0xf3, 0xdf, 0x80, 0xda, 0x2d, 0xc3, 0x5d, 0xae, 677 | 0xff, 0xc2, 0x78, 0xf7, 0x4e, 0xcb, 0x42, 0x4b, 0x3b, 0x1f, 0xa5, 0x68, 0x8c, 0x33, 0xe6, 0xed, 678 | 0xda, 0x2a, 0x46, 0x8b, 0x2e, 0xf3, 0x59, 0xd0, 0x19, 0x9e, 0xc2, 0xe6, 0xc6, 0xf5, 0x38, 0xa8, 679 | 0x59, 0x52, 0x07, 0x06, 0xea, 0x36, 0x54, 0x03, 0xb8, 0x9d, 0x3c, 0x52, 0x64, 0x6f, 0xc8, 0x62, 680 | 0xb8, 0x5e, 0x70, 0x8e, 0x78, 0xab, 0xc2, 0xb4, 0x24, 0xb7, 0xe1, 0xb3, 0xa0, 0x15, 0x2e, 0x1f, 681 | 0xce, 0x31, 0xef, 0x26, 0x69, 0x31, 0xc1, 0xf4, 0x8a, 0x1e, 0xb0, 0x4c, 0xad, 0xdb, 0xf4, 0x59, 682 | 0xd0, 0x0e, 0xb7, 0x43, 0xc7, 0xe7, 0x9d, 0x98, 0x4c, 0xa4, 0xa5, 0xb2, 0xb2, 0xc8, 0xdd, 0xff, 683 | 0x3e, 0x0b, 0x0e, 0xc2, 0xaf, 0x51, 0xff, 0x99, 0xf1, 0xc3, 0x2d, 0xfb, 0xb1, 0x34, 0xd6, 0xb9, 684 | 0xde, 0xbb, 0x00, 0xfe, 0x76, 0x41, 0x4d, 0xef, 0xf8, 0x8f, 0x78, 0x4b, 0x5a, 0xca, 0x8c, 0xdb, 685 | 0xf0, 0x9b, 0x41, 0x67, 0x78, 0x02, 0x3f, 0x7f, 0x37, 0x6c, 0xd9, 0x84, 0x4b, 0xf6, 0xb2, 0xf7, 686 | 0xba, 0xf0, 0xd8, 0xdb, 0xc2, 0x63, 0xef, 0x0b, 0x8f, 0x3d, 0x7d, 0x78, 0xff, 0xee, 0xdb, 0x2b, 687 | 0xe0, 0x33, 0x00, 0x00, 0xff, 0xff, 0x08, 0xe5, 0xc1, 0x32, 0x70, 0x02, 0x00, 0x00, 688 | } 689 | -------------------------------------------------------------------------------- /apis/scheduling/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("scheduling.k8s.io", "v1alpha1", "priorityclasses", false, &PriorityClass{}) 7 | 8 | k8s.RegisterList("scheduling.k8s.io", "v1alpha1", "priorityclasses", false, &PriorityClassList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/scheduling/v1beta1/generated.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: k8s.io/api/scheduling/v1beta1/generated.proto 3 | 4 | /* 5 | Package v1beta1 is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | k8s.io/api/scheduling/v1beta1/generated.proto 9 | 10 | It has these top-level messages: 11 | PriorityClass 12 | PriorityClassList 13 | */ 14 | package v1beta1 15 | 16 | import proto "github.com/golang/protobuf/proto" 17 | import fmt "fmt" 18 | import math "math" 19 | import k8s_io_apimachinery_pkg_apis_meta_v1 "github.com/ericchiang/k8s/apis/meta/v1" 20 | import _ "github.com/ericchiang/k8s/runtime" 21 | import _ "github.com/ericchiang/k8s/runtime/schema" 22 | 23 | import io "io" 24 | 25 | // Reference imports to suppress errors if they are not otherwise used. 26 | var _ = proto.Marshal 27 | var _ = fmt.Errorf 28 | var _ = math.Inf 29 | 30 | // This is a compile-time assertion to ensure that this generated file 31 | // is compatible with the proto package it is being compiled against. 32 | // A compilation error at this line likely means your copy of the 33 | // proto package needs to be updated. 34 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 35 | 36 | // PriorityClass defines mapping from a priority class name to the priority 37 | // integer value. The value can be any valid integer. 38 | type PriorityClass struct { 39 | // Standard object's metadata. 40 | // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata 41 | // +optional 42 | Metadata *k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta `protobuf:"bytes,1,opt,name=metadata" json:"metadata,omitempty"` 43 | // The value of this priority class. This is the actual priority that pods 44 | // receive when they have the name of this class in their pod spec. 45 | Value *int32 `protobuf:"varint,2,opt,name=value" json:"value,omitempty"` 46 | // globalDefault specifies whether this PriorityClass should be considered as 47 | // the default priority for pods that do not have any priority class. 48 | // Only one PriorityClass can be marked as `globalDefault`. However, if more than 49 | // one PriorityClasses exists with their `globalDefault` field set to true, 50 | // the smallest value of such global default PriorityClasses will be used as the default priority. 51 | // +optional 52 | GlobalDefault *bool `protobuf:"varint,3,opt,name=globalDefault" json:"globalDefault,omitempty"` 53 | // description is an arbitrary string that usually provides guidelines on 54 | // when this priority class should be used. 55 | // +optional 56 | Description *string `protobuf:"bytes,4,opt,name=description" json:"description,omitempty"` 57 | XXX_unrecognized []byte `json:"-"` 58 | } 59 | 60 | func (m *PriorityClass) Reset() { *m = PriorityClass{} } 61 | func (m *PriorityClass) String() string { return proto.CompactTextString(m) } 62 | func (*PriorityClass) ProtoMessage() {} 63 | func (*PriorityClass) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} } 64 | 65 | func (m *PriorityClass) GetMetadata() *k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta { 66 | if m != nil { 67 | return m.Metadata 68 | } 69 | return nil 70 | } 71 | 72 | func (m *PriorityClass) GetValue() int32 { 73 | if m != nil && m.Value != nil { 74 | return *m.Value 75 | } 76 | return 0 77 | } 78 | 79 | func (m *PriorityClass) GetGlobalDefault() bool { 80 | if m != nil && m.GlobalDefault != nil { 81 | return *m.GlobalDefault 82 | } 83 | return false 84 | } 85 | 86 | func (m *PriorityClass) GetDescription() string { 87 | if m != nil && m.Description != nil { 88 | return *m.Description 89 | } 90 | return "" 91 | } 92 | 93 | // PriorityClassList is a collection of priority classes. 94 | type PriorityClassList struct { 95 | // Standard list metadata 96 | // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata 97 | // +optional 98 | Metadata *k8s_io_apimachinery_pkg_apis_meta_v1.ListMeta `protobuf:"bytes,1,opt,name=metadata" json:"metadata,omitempty"` 99 | // items is the list of PriorityClasses 100 | Items []*PriorityClass `protobuf:"bytes,2,rep,name=items" json:"items,omitempty"` 101 | XXX_unrecognized []byte `json:"-"` 102 | } 103 | 104 | func (m *PriorityClassList) Reset() { *m = PriorityClassList{} } 105 | func (m *PriorityClassList) String() string { return proto.CompactTextString(m) } 106 | func (*PriorityClassList) ProtoMessage() {} 107 | func (*PriorityClassList) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{1} } 108 | 109 | func (m *PriorityClassList) GetMetadata() *k8s_io_apimachinery_pkg_apis_meta_v1.ListMeta { 110 | if m != nil { 111 | return m.Metadata 112 | } 113 | return nil 114 | } 115 | 116 | func (m *PriorityClassList) GetItems() []*PriorityClass { 117 | if m != nil { 118 | return m.Items 119 | } 120 | return nil 121 | } 122 | 123 | func init() { 124 | proto.RegisterType((*PriorityClass)(nil), "k8s.io.api.scheduling.v1beta1.PriorityClass") 125 | proto.RegisterType((*PriorityClassList)(nil), "k8s.io.api.scheduling.v1beta1.PriorityClassList") 126 | } 127 | func (m *PriorityClass) Marshal() (dAtA []byte, err error) { 128 | size := m.Size() 129 | dAtA = make([]byte, size) 130 | n, err := m.MarshalTo(dAtA) 131 | if err != nil { 132 | return nil, err 133 | } 134 | return dAtA[:n], nil 135 | } 136 | 137 | func (m *PriorityClass) MarshalTo(dAtA []byte) (int, error) { 138 | var i int 139 | _ = i 140 | var l int 141 | _ = l 142 | if m.Metadata != nil { 143 | dAtA[i] = 0xa 144 | i++ 145 | i = encodeVarintGenerated(dAtA, i, uint64(m.Metadata.Size())) 146 | n1, err := m.Metadata.MarshalTo(dAtA[i:]) 147 | if err != nil { 148 | return 0, err 149 | } 150 | i += n1 151 | } 152 | if m.Value != nil { 153 | dAtA[i] = 0x10 154 | i++ 155 | i = encodeVarintGenerated(dAtA, i, uint64(*m.Value)) 156 | } 157 | if m.GlobalDefault != nil { 158 | dAtA[i] = 0x18 159 | i++ 160 | if *m.GlobalDefault { 161 | dAtA[i] = 1 162 | } else { 163 | dAtA[i] = 0 164 | } 165 | i++ 166 | } 167 | if m.Description != nil { 168 | dAtA[i] = 0x22 169 | i++ 170 | i = encodeVarintGenerated(dAtA, i, uint64(len(*m.Description))) 171 | i += copy(dAtA[i:], *m.Description) 172 | } 173 | if m.XXX_unrecognized != nil { 174 | i += copy(dAtA[i:], m.XXX_unrecognized) 175 | } 176 | return i, nil 177 | } 178 | 179 | func (m *PriorityClassList) Marshal() (dAtA []byte, err error) { 180 | size := m.Size() 181 | dAtA = make([]byte, size) 182 | n, err := m.MarshalTo(dAtA) 183 | if err != nil { 184 | return nil, err 185 | } 186 | return dAtA[:n], nil 187 | } 188 | 189 | func (m *PriorityClassList) MarshalTo(dAtA []byte) (int, error) { 190 | var i int 191 | _ = i 192 | var l int 193 | _ = l 194 | if m.Metadata != nil { 195 | dAtA[i] = 0xa 196 | i++ 197 | i = encodeVarintGenerated(dAtA, i, uint64(m.Metadata.Size())) 198 | n2, err := m.Metadata.MarshalTo(dAtA[i:]) 199 | if err != nil { 200 | return 0, err 201 | } 202 | i += n2 203 | } 204 | if len(m.Items) > 0 { 205 | for _, msg := range m.Items { 206 | dAtA[i] = 0x12 207 | i++ 208 | i = encodeVarintGenerated(dAtA, i, uint64(msg.Size())) 209 | n, err := msg.MarshalTo(dAtA[i:]) 210 | if err != nil { 211 | return 0, err 212 | } 213 | i += n 214 | } 215 | } 216 | if m.XXX_unrecognized != nil { 217 | i += copy(dAtA[i:], m.XXX_unrecognized) 218 | } 219 | return i, nil 220 | } 221 | 222 | func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int { 223 | for v >= 1<<7 { 224 | dAtA[offset] = uint8(v&0x7f | 0x80) 225 | v >>= 7 226 | offset++ 227 | } 228 | dAtA[offset] = uint8(v) 229 | return offset + 1 230 | } 231 | func (m *PriorityClass) Size() (n int) { 232 | var l int 233 | _ = l 234 | if m.Metadata != nil { 235 | l = m.Metadata.Size() 236 | n += 1 + l + sovGenerated(uint64(l)) 237 | } 238 | if m.Value != nil { 239 | n += 1 + sovGenerated(uint64(*m.Value)) 240 | } 241 | if m.GlobalDefault != nil { 242 | n += 2 243 | } 244 | if m.Description != nil { 245 | l = len(*m.Description) 246 | n += 1 + l + sovGenerated(uint64(l)) 247 | } 248 | if m.XXX_unrecognized != nil { 249 | n += len(m.XXX_unrecognized) 250 | } 251 | return n 252 | } 253 | 254 | func (m *PriorityClassList) Size() (n int) { 255 | var l int 256 | _ = l 257 | if m.Metadata != nil { 258 | l = m.Metadata.Size() 259 | n += 1 + l + sovGenerated(uint64(l)) 260 | } 261 | if len(m.Items) > 0 { 262 | for _, e := range m.Items { 263 | l = e.Size() 264 | n += 1 + l + sovGenerated(uint64(l)) 265 | } 266 | } 267 | if m.XXX_unrecognized != nil { 268 | n += len(m.XXX_unrecognized) 269 | } 270 | return n 271 | } 272 | 273 | func sovGenerated(x uint64) (n int) { 274 | for { 275 | n++ 276 | x >>= 7 277 | if x == 0 { 278 | break 279 | } 280 | } 281 | return n 282 | } 283 | func sozGenerated(x uint64) (n int) { 284 | return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63)))) 285 | } 286 | func (m *PriorityClass) Unmarshal(dAtA []byte) error { 287 | l := len(dAtA) 288 | iNdEx := 0 289 | for iNdEx < l { 290 | preIndex := iNdEx 291 | var wire uint64 292 | for shift := uint(0); ; shift += 7 { 293 | if shift >= 64 { 294 | return ErrIntOverflowGenerated 295 | } 296 | if iNdEx >= l { 297 | return io.ErrUnexpectedEOF 298 | } 299 | b := dAtA[iNdEx] 300 | iNdEx++ 301 | wire |= (uint64(b) & 0x7F) << shift 302 | if b < 0x80 { 303 | break 304 | } 305 | } 306 | fieldNum := int32(wire >> 3) 307 | wireType := int(wire & 0x7) 308 | if wireType == 4 { 309 | return fmt.Errorf("proto: PriorityClass: wiretype end group for non-group") 310 | } 311 | if fieldNum <= 0 { 312 | return fmt.Errorf("proto: PriorityClass: illegal tag %d (wire type %d)", fieldNum, wire) 313 | } 314 | switch fieldNum { 315 | case 1: 316 | if wireType != 2 { 317 | return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) 318 | } 319 | var msglen int 320 | for shift := uint(0); ; shift += 7 { 321 | if shift >= 64 { 322 | return ErrIntOverflowGenerated 323 | } 324 | if iNdEx >= l { 325 | return io.ErrUnexpectedEOF 326 | } 327 | b := dAtA[iNdEx] 328 | iNdEx++ 329 | msglen |= (int(b) & 0x7F) << shift 330 | if b < 0x80 { 331 | break 332 | } 333 | } 334 | if msglen < 0 { 335 | return ErrInvalidLengthGenerated 336 | } 337 | postIndex := iNdEx + msglen 338 | if postIndex > l { 339 | return io.ErrUnexpectedEOF 340 | } 341 | if m.Metadata == nil { 342 | m.Metadata = &k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta{} 343 | } 344 | if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { 345 | return err 346 | } 347 | iNdEx = postIndex 348 | case 2: 349 | if wireType != 0 { 350 | return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) 351 | } 352 | var v int32 353 | for shift := uint(0); ; shift += 7 { 354 | if shift >= 64 { 355 | return ErrIntOverflowGenerated 356 | } 357 | if iNdEx >= l { 358 | return io.ErrUnexpectedEOF 359 | } 360 | b := dAtA[iNdEx] 361 | iNdEx++ 362 | v |= (int32(b) & 0x7F) << shift 363 | if b < 0x80 { 364 | break 365 | } 366 | } 367 | m.Value = &v 368 | case 3: 369 | if wireType != 0 { 370 | return fmt.Errorf("proto: wrong wireType = %d for field GlobalDefault", wireType) 371 | } 372 | var v int 373 | for shift := uint(0); ; shift += 7 { 374 | if shift >= 64 { 375 | return ErrIntOverflowGenerated 376 | } 377 | if iNdEx >= l { 378 | return io.ErrUnexpectedEOF 379 | } 380 | b := dAtA[iNdEx] 381 | iNdEx++ 382 | v |= (int(b) & 0x7F) << shift 383 | if b < 0x80 { 384 | break 385 | } 386 | } 387 | b := bool(v != 0) 388 | m.GlobalDefault = &b 389 | case 4: 390 | if wireType != 2 { 391 | return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) 392 | } 393 | var stringLen uint64 394 | for shift := uint(0); ; shift += 7 { 395 | if shift >= 64 { 396 | return ErrIntOverflowGenerated 397 | } 398 | if iNdEx >= l { 399 | return io.ErrUnexpectedEOF 400 | } 401 | b := dAtA[iNdEx] 402 | iNdEx++ 403 | stringLen |= (uint64(b) & 0x7F) << shift 404 | if b < 0x80 { 405 | break 406 | } 407 | } 408 | intStringLen := int(stringLen) 409 | if intStringLen < 0 { 410 | return ErrInvalidLengthGenerated 411 | } 412 | postIndex := iNdEx + intStringLen 413 | if postIndex > l { 414 | return io.ErrUnexpectedEOF 415 | } 416 | s := string(dAtA[iNdEx:postIndex]) 417 | m.Description = &s 418 | iNdEx = postIndex 419 | default: 420 | iNdEx = preIndex 421 | skippy, err := skipGenerated(dAtA[iNdEx:]) 422 | if err != nil { 423 | return err 424 | } 425 | if skippy < 0 { 426 | return ErrInvalidLengthGenerated 427 | } 428 | if (iNdEx + skippy) > l { 429 | return io.ErrUnexpectedEOF 430 | } 431 | m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) 432 | iNdEx += skippy 433 | } 434 | } 435 | 436 | if iNdEx > l { 437 | return io.ErrUnexpectedEOF 438 | } 439 | return nil 440 | } 441 | func (m *PriorityClassList) Unmarshal(dAtA []byte) error { 442 | l := len(dAtA) 443 | iNdEx := 0 444 | for iNdEx < l { 445 | preIndex := iNdEx 446 | var wire uint64 447 | for shift := uint(0); ; shift += 7 { 448 | if shift >= 64 { 449 | return ErrIntOverflowGenerated 450 | } 451 | if iNdEx >= l { 452 | return io.ErrUnexpectedEOF 453 | } 454 | b := dAtA[iNdEx] 455 | iNdEx++ 456 | wire |= (uint64(b) & 0x7F) << shift 457 | if b < 0x80 { 458 | break 459 | } 460 | } 461 | fieldNum := int32(wire >> 3) 462 | wireType := int(wire & 0x7) 463 | if wireType == 4 { 464 | return fmt.Errorf("proto: PriorityClassList: wiretype end group for non-group") 465 | } 466 | if fieldNum <= 0 { 467 | return fmt.Errorf("proto: PriorityClassList: illegal tag %d (wire type %d)", fieldNum, wire) 468 | } 469 | switch fieldNum { 470 | case 1: 471 | if wireType != 2 { 472 | return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) 473 | } 474 | var msglen int 475 | for shift := uint(0); ; shift += 7 { 476 | if shift >= 64 { 477 | return ErrIntOverflowGenerated 478 | } 479 | if iNdEx >= l { 480 | return io.ErrUnexpectedEOF 481 | } 482 | b := dAtA[iNdEx] 483 | iNdEx++ 484 | msglen |= (int(b) & 0x7F) << shift 485 | if b < 0x80 { 486 | break 487 | } 488 | } 489 | if msglen < 0 { 490 | return ErrInvalidLengthGenerated 491 | } 492 | postIndex := iNdEx + msglen 493 | if postIndex > l { 494 | return io.ErrUnexpectedEOF 495 | } 496 | if m.Metadata == nil { 497 | m.Metadata = &k8s_io_apimachinery_pkg_apis_meta_v1.ListMeta{} 498 | } 499 | if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { 500 | return err 501 | } 502 | iNdEx = postIndex 503 | case 2: 504 | if wireType != 2 { 505 | return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) 506 | } 507 | var msglen int 508 | for shift := uint(0); ; shift += 7 { 509 | if shift >= 64 { 510 | return ErrIntOverflowGenerated 511 | } 512 | if iNdEx >= l { 513 | return io.ErrUnexpectedEOF 514 | } 515 | b := dAtA[iNdEx] 516 | iNdEx++ 517 | msglen |= (int(b) & 0x7F) << shift 518 | if b < 0x80 { 519 | break 520 | } 521 | } 522 | if msglen < 0 { 523 | return ErrInvalidLengthGenerated 524 | } 525 | postIndex := iNdEx + msglen 526 | if postIndex > l { 527 | return io.ErrUnexpectedEOF 528 | } 529 | m.Items = append(m.Items, &PriorityClass{}) 530 | if err := m.Items[len(m.Items)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { 531 | return err 532 | } 533 | iNdEx = postIndex 534 | default: 535 | iNdEx = preIndex 536 | skippy, err := skipGenerated(dAtA[iNdEx:]) 537 | if err != nil { 538 | return err 539 | } 540 | if skippy < 0 { 541 | return ErrInvalidLengthGenerated 542 | } 543 | if (iNdEx + skippy) > l { 544 | return io.ErrUnexpectedEOF 545 | } 546 | m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) 547 | iNdEx += skippy 548 | } 549 | } 550 | 551 | if iNdEx > l { 552 | return io.ErrUnexpectedEOF 553 | } 554 | return nil 555 | } 556 | func skipGenerated(dAtA []byte) (n int, err error) { 557 | l := len(dAtA) 558 | iNdEx := 0 559 | for iNdEx < l { 560 | var wire uint64 561 | for shift := uint(0); ; shift += 7 { 562 | if shift >= 64 { 563 | return 0, ErrIntOverflowGenerated 564 | } 565 | if iNdEx >= l { 566 | return 0, io.ErrUnexpectedEOF 567 | } 568 | b := dAtA[iNdEx] 569 | iNdEx++ 570 | wire |= (uint64(b) & 0x7F) << shift 571 | if b < 0x80 { 572 | break 573 | } 574 | } 575 | wireType := int(wire & 0x7) 576 | switch wireType { 577 | case 0: 578 | for shift := uint(0); ; shift += 7 { 579 | if shift >= 64 { 580 | return 0, ErrIntOverflowGenerated 581 | } 582 | if iNdEx >= l { 583 | return 0, io.ErrUnexpectedEOF 584 | } 585 | iNdEx++ 586 | if dAtA[iNdEx-1] < 0x80 { 587 | break 588 | } 589 | } 590 | return iNdEx, nil 591 | case 1: 592 | iNdEx += 8 593 | return iNdEx, nil 594 | case 2: 595 | var length int 596 | for shift := uint(0); ; shift += 7 { 597 | if shift >= 64 { 598 | return 0, ErrIntOverflowGenerated 599 | } 600 | if iNdEx >= l { 601 | return 0, io.ErrUnexpectedEOF 602 | } 603 | b := dAtA[iNdEx] 604 | iNdEx++ 605 | length |= (int(b) & 0x7F) << shift 606 | if b < 0x80 { 607 | break 608 | } 609 | } 610 | iNdEx += length 611 | if length < 0 { 612 | return 0, ErrInvalidLengthGenerated 613 | } 614 | return iNdEx, nil 615 | case 3: 616 | for { 617 | var innerWire uint64 618 | var start int = iNdEx 619 | for shift := uint(0); ; shift += 7 { 620 | if shift >= 64 { 621 | return 0, ErrIntOverflowGenerated 622 | } 623 | if iNdEx >= l { 624 | return 0, io.ErrUnexpectedEOF 625 | } 626 | b := dAtA[iNdEx] 627 | iNdEx++ 628 | innerWire |= (uint64(b) & 0x7F) << shift 629 | if b < 0x80 { 630 | break 631 | } 632 | } 633 | innerWireType := int(innerWire & 0x7) 634 | if innerWireType == 4 { 635 | break 636 | } 637 | next, err := skipGenerated(dAtA[start:]) 638 | if err != nil { 639 | return 0, err 640 | } 641 | iNdEx = start + next 642 | } 643 | return iNdEx, nil 644 | case 4: 645 | return iNdEx, nil 646 | case 5: 647 | iNdEx += 4 648 | return iNdEx, nil 649 | default: 650 | return 0, fmt.Errorf("proto: illegal wireType %d", wireType) 651 | } 652 | } 653 | panic("unreachable") 654 | } 655 | 656 | var ( 657 | ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling") 658 | ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow") 659 | ) 660 | 661 | func init() { 662 | proto.RegisterFile("k8s.io/api/scheduling/v1beta1/generated.proto", fileDescriptorGenerated) 663 | } 664 | 665 | var fileDescriptorGenerated = []byte{ 666 | // 335 bytes of a gzipped FileDescriptorProto 667 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0xcd, 0x4a, 0xf3, 0x40, 668 | 0x14, 0x86, 0xbf, 0x69, 0xbf, 0x62, 0x9d, 0xd2, 0x85, 0xc1, 0x45, 0x2c, 0x18, 0x42, 0x71, 0x91, 669 | 0x85, 0xce, 0xd8, 0x22, 0xe2, 0xba, 0xba, 0x92, 0x8a, 0x92, 0xa5, 0xbb, 0xd3, 0xe4, 0x98, 0x8e, 670 | 0xcd, 0x1f, 0x33, 0x27, 0x81, 0xde, 0x89, 0x7b, 0x2f, 0xc3, 0x1b, 0x70, 0xe9, 0x25, 0x48, 0xbd, 671 | 0x11, 0x09, 0x2d, 0xad, 0x6d, 0xf1, 0x67, 0x39, 0x2f, 0xef, 0xf3, 0xf2, 0x1c, 0x86, 0x9f, 0x4c, 672 | 0x2e, 0x8c, 0x50, 0x99, 0x84, 0x5c, 0x49, 0x13, 0x8c, 0x31, 0x2c, 0x62, 0x95, 0x46, 0xb2, 0xec, 673 | 0x8d, 0x90, 0xa0, 0x27, 0x23, 0x4c, 0x51, 0x03, 0x61, 0x28, 0x72, 0x9d, 0x51, 0x66, 0x1d, 0xce, 674 | 0xeb, 0x02, 0x72, 0x25, 0x56, 0x75, 0xb1, 0xa8, 0x77, 0xce, 0x56, 0x6b, 0x09, 0x04, 0x63, 0x95, 675 | 0xa2, 0x9e, 0xca, 0x7c, 0x12, 0x55, 0x81, 0x91, 0x09, 0x12, 0xc8, 0x72, 0x6b, 0xb4, 0x23, 0xbf, 676 | 0xa3, 0x74, 0x91, 0x92, 0x4a, 0x70, 0x0b, 0x38, 0xff, 0x0d, 0xa8, 0xd4, 0x12, 0xd8, 0xe4, 0xba, 677 | 0x2f, 0x8c, 0xb7, 0xef, 0xb4, 0xca, 0xb4, 0xa2, 0xe9, 0x65, 0x0c, 0xc6, 0x58, 0x43, 0xde, 0xac, 678 | 0xac, 0x42, 0x20, 0xb0, 0x99, 0xcb, 0xbc, 0x56, 0xff, 0x54, 0xac, 0x4e, 0x5c, 0x8e, 0x8b, 0x7c, 679 | 0x12, 0x55, 0x81, 0x11, 0x55, 0x5b, 0x94, 0x3d, 0x71, 0x3b, 0x7a, 0xc4, 0x80, 0x6e, 0x90, 0xc0, 680 | 0x5f, 0x2e, 0x58, 0xfb, 0xbc, 0x51, 0x42, 0x5c, 0xa0, 0x5d, 0x73, 0x99, 0xd7, 0xf0, 0xe7, 0x0f, 681 | 0xeb, 0x88, 0xb7, 0xa3, 0x38, 0x1b, 0x41, 0x7c, 0x85, 0x0f, 0x50, 0xc4, 0x64, 0xd7, 0x5d, 0xe6, 682 | 0x35, 0xfd, 0xf5, 0xd0, 0x72, 0x79, 0x2b, 0x44, 0x13, 0x68, 0x95, 0x93, 0xca, 0x52, 0xfb, 0xbf, 683 | 0xcb, 0xbc, 0x5d, 0xff, 0x6b, 0xd4, 0x7d, 0x66, 0x7c, 0x6f, 0xcd, 0x7e, 0xa8, 0x0c, 0x59, 0xd7, 684 | 0x5b, 0x17, 0x88, 0xbf, 0x5d, 0x50, 0xd1, 0x1b, 0xfe, 0x03, 0xde, 0x50, 0x84, 0x89, 0xb1, 0x6b, 685 | 0x6e, 0xdd, 0x6b, 0xf5, 0x8f, 0xc5, 0x8f, 0xbf, 0x2d, 0xd6, 0x64, 0xfc, 0x39, 0x3a, 0x38, 0x78, 686 | 0x9d, 0x39, 0xec, 0x6d, 0xe6, 0xb0, 0xf7, 0x99, 0xc3, 0x9e, 0x3e, 0x9c, 0x7f, 0xf7, 0x3b, 0x8b, 687 | 0xfe, 0x67, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd3, 0x01, 0xa6, 0x2c, 0x6c, 0x02, 0x00, 0x00, 688 | } 689 | -------------------------------------------------------------------------------- /apis/settings/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("settings.k8s.io", "v1alpha1", "podpresets", true, &PodPreset{}) 7 | 8 | k8s.RegisterList("settings.k8s.io", "v1alpha1", "podpresets", true, &PodPresetList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/storage/v1/register.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("storage.k8s.io", "v1", "storageclasses", false, &StorageClass{}) 7 | 8 | k8s.RegisterList("storage.k8s.io", "v1", "storageclasses", false, &StorageClassList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/storage/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("storage.k8s.io", "v1alpha1", "volumeattachments", false, &VolumeAttachment{}) 7 | 8 | k8s.RegisterList("storage.k8s.io", "v1alpha1", "volumeattachments", false, &VolumeAttachmentList{}) 9 | } 10 | -------------------------------------------------------------------------------- /apis/storage/v1beta1/register.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import "github.com/ericchiang/k8s" 4 | 5 | func init() { 6 | k8s.Register("storage.k8s.io", "v1beta1", "storageclasses", false, &StorageClass{}) 7 | 8 | k8s.RegisterList("storage.k8s.io", "v1beta1", "storageclasses", false, &StorageClassList{}) 9 | } 10 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package k8s implements a Kubernetes client. 3 | 4 | import ( 5 | "context" 6 | 7 | "github.com/ericchiang/k8s" 8 | appsv1 "github.com/ericchiang/k8s/apis/apps/v1" 9 | ) 10 | 11 | func listDeployments(ctx context.Context) (*appsv1.DeploymentList, error) { 12 | c, err := k8s.NewInClusterClient() 13 | if err != nil { 14 | return nil, err 15 | } 16 | 17 | var deployments appsv1.DeploymentList 18 | if err := c.List(ctx, "my-namespace", &deployments); err != nil { 19 | return nil, err 20 | } 21 | return deployments, nil 22 | } 23 | 24 | */ 25 | package k8s 26 | 27 | import ( 28 | "bytes" 29 | "context" 30 | "crypto/tls" 31 | "crypto/x509" 32 | "encoding/base64" 33 | "errors" 34 | "fmt" 35 | "io" 36 | "io/ioutil" 37 | "net" 38 | "net/http" 39 | "net/url" 40 | "os" 41 | "time" 42 | 43 | "golang.org/x/net/http2" 44 | 45 | metav1 "github.com/ericchiang/k8s/apis/meta/v1" 46 | ) 47 | 48 | const ( 49 | // AllNamespaces is given to list and watch operations to signify that the code should 50 | // list or watch resources in all namespaces. 51 | AllNamespaces = allNamespaces 52 | // Actual definition is private in case we want to change it later. 53 | allNamespaces = "" 54 | 55 | namespaceDefault = "default" 56 | ) 57 | 58 | // String returns a pointer to a string. Useful for creating API objects 59 | // that take pointers instead of literals. 60 | func String(s string) *string { return &s } 61 | 62 | // Int is a convenience for converting an int literal to a pointer to an int. 63 | func Int(i int) *int { return &i } 64 | 65 | // Int32 is a convenience for converting an int32 literal to a pointer to an int32. 66 | func Int32(i int32) *int32 { return &i } 67 | 68 | // Bool is a convenience for converting a bool literal to a pointer to a bool. 69 | func Bool(b bool) *bool { return &b } 70 | 71 | const ( 72 | // Types for watch events. 73 | EventAdded = "ADDED" 74 | EventDeleted = "DELETED" 75 | EventModified = "MODIFIED" 76 | EventError = "ERROR" 77 | ) 78 | 79 | // Client is a Kuberntes client. 80 | type Client struct { 81 | // The URL of the API server. 82 | Endpoint string 83 | 84 | // Namespace is the name fo the default reconciled from the client's config. 85 | // It is set when constructing a client using NewClient(), and defaults to 86 | // the value "default". 87 | // 88 | // This value should be used to access the client's default namespace. For 89 | // example, to create a configmap in the default namespace, use client.Namespace 90 | // when to fill the ObjectMeta: 91 | // 92 | // client, err := k8s.NewClient(config) 93 | // if err != nil { 94 | // // handle error 95 | // } 96 | // cm := v1.ConfigMap{ 97 | // Metadata: &metav1.ObjectMeta{ 98 | // Name: &k8s.String("my-configmap"), 99 | // Namespace: &client.Namespace, 100 | // }, 101 | // Data: map[string]string{"foo": "bar", "spam": "eggs"}, 102 | // } 103 | // err := client.Create(ctx, cm) 104 | // 105 | Namespace string 106 | 107 | // SetHeaders provides a hook for modifying the HTTP headers of all requests. 108 | // 109 | // client, err := k8s.NewClient(config) 110 | // if err != nil { 111 | // // handle error 112 | // } 113 | // client.SetHeaders = func(h http.Header) error { 114 | // h.Set("Authorization", "Bearer "+mytoken) 115 | // return nil 116 | // } 117 | // 118 | SetHeaders func(h http.Header) error 119 | 120 | Client *http.Client 121 | } 122 | 123 | func (c *Client) newRequest(ctx context.Context, verb, url string, body io.Reader) (*http.Request, error) { 124 | req, err := http.NewRequest(verb, url, body) 125 | if err != nil { 126 | return nil, err 127 | } 128 | if c.SetHeaders != nil { 129 | if err := c.SetHeaders(req.Header); err != nil { 130 | return nil, err 131 | } 132 | } 133 | return req.WithContext(ctx), nil 134 | } 135 | 136 | // NewClient initializes a client from a client config. 137 | func NewClient(config *Config) (*Client, error) { 138 | if len(config.Contexts) == 0 { 139 | if config.CurrentContext != "" { 140 | return nil, fmt.Errorf("no contexts with name %q", config.CurrentContext) 141 | } 142 | 143 | if n := len(config.Clusters); n == 0 { 144 | return nil, errors.New("no clusters provided") 145 | } else if n > 1 { 146 | return nil, errors.New("multiple clusters but no current context") 147 | } 148 | if n := len(config.AuthInfos); n == 0 { 149 | return nil, errors.New("no users provided") 150 | } else if n > 1 { 151 | return nil, errors.New("multiple users but no current context") 152 | } 153 | 154 | return newClient(config.Clusters[0].Cluster, config.AuthInfos[0].AuthInfo, namespaceDefault) 155 | } 156 | 157 | var ctx Context 158 | if config.CurrentContext == "" { 159 | if n := len(config.Contexts); n == 0 { 160 | return nil, errors.New("no contexts provided") 161 | } else if n > 1 { 162 | return nil, errors.New("multiple contexts but no current context") 163 | } 164 | ctx = config.Contexts[0].Context 165 | } else { 166 | for _, c := range config.Contexts { 167 | if c.Name == config.CurrentContext { 168 | ctx = c.Context 169 | goto configFound 170 | } 171 | } 172 | return nil, fmt.Errorf("no config named %q", config.CurrentContext) 173 | configFound: 174 | } 175 | 176 | if ctx.Cluster == "" { 177 | return nil, fmt.Errorf("context doesn't have a cluster") 178 | } 179 | if ctx.AuthInfo == "" { 180 | return nil, fmt.Errorf("context doesn't have a user") 181 | } 182 | var ( 183 | user AuthInfo 184 | cluster Cluster 185 | ) 186 | 187 | for _, u := range config.AuthInfos { 188 | if u.Name == ctx.AuthInfo { 189 | user = u.AuthInfo 190 | goto userFound 191 | } 192 | } 193 | return nil, fmt.Errorf("no user named %q", ctx.AuthInfo) 194 | userFound: 195 | 196 | for _, c := range config.Clusters { 197 | if c.Name == ctx.Cluster { 198 | cluster = c.Cluster 199 | goto clusterFound 200 | } 201 | } 202 | return nil, fmt.Errorf("no cluster named %q", ctx.Cluster) 203 | clusterFound: 204 | 205 | namespace := ctx.Namespace 206 | if namespace == "" { 207 | namespace = namespaceDefault 208 | } 209 | 210 | return newClient(cluster, user, namespace) 211 | } 212 | 213 | // NewInClusterClient returns a client that uses the service account bearer token mounted 214 | // into Kubernetes pods. 215 | func NewInClusterClient() (*Client, error) { 216 | host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT") 217 | if len(host) == 0 || len(port) == 0 { 218 | return nil, errors.New("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined") 219 | } 220 | namespace, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace") 221 | if err != nil { 222 | return nil, err 223 | } 224 | 225 | server := url.URL{ 226 | Scheme: "https", 227 | Host: net.JoinHostPort(host, port), 228 | } 229 | cluster := Cluster{ 230 | Server: server.String(), 231 | CertificateAuthority: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt", 232 | } 233 | user := AuthInfo{TokenFile: "/var/run/secrets/kubernetes.io/serviceaccount/token"} 234 | return newClient(cluster, user, string(namespace)) 235 | } 236 | 237 | func load(filepath string, data []byte) (out []byte, err error) { 238 | if filepath != "" { 239 | data, err = ioutil.ReadFile(filepath) 240 | } 241 | return data, err 242 | } 243 | 244 | func newClient(cluster Cluster, user AuthInfo, namespace string) (*Client, error) { 245 | if cluster.Server == "" { 246 | // NOTE: kubectl defaults to localhost:8080, but it's probably better to just 247 | // be strict. 248 | return nil, fmt.Errorf("no cluster endpoint provided") 249 | } 250 | 251 | ca, err := load(cluster.CertificateAuthority, cluster.CertificateAuthorityData) 252 | if err != nil { 253 | return nil, fmt.Errorf("loading certificate authority: %v", err) 254 | } 255 | 256 | clientCert, err := load(user.ClientCertificate, user.ClientCertificateData) 257 | if err != nil { 258 | return nil, fmt.Errorf("load client cert: %v", err) 259 | } 260 | clientKey, err := load(user.ClientKey, user.ClientKeyData) 261 | if err != nil { 262 | return nil, fmt.Errorf("load client cert: %v", err) 263 | } 264 | 265 | // See https://github.com/gtank/cryptopasta 266 | tlsConfig := &tls.Config{ 267 | MinVersion: tls.VersionTLS12, 268 | InsecureSkipVerify: cluster.InsecureSkipTLSVerify, 269 | } 270 | 271 | if len(ca) != 0 { 272 | tlsConfig.RootCAs = x509.NewCertPool() 273 | if !tlsConfig.RootCAs.AppendCertsFromPEM(ca) { 274 | return nil, errors.New("certificate authority doesn't contain any certificates") 275 | } 276 | } 277 | if len(clientCert) != 0 { 278 | cert, err := tls.X509KeyPair(clientCert, clientKey) 279 | if err != nil { 280 | return nil, fmt.Errorf("invalid client cert and key pair: %v", err) 281 | } 282 | tlsConfig.Certificates = []tls.Certificate{cert} 283 | } 284 | 285 | token := user.Token 286 | if user.TokenFile != "" { 287 | data, err := ioutil.ReadFile(user.TokenFile) 288 | if err != nil { 289 | return nil, fmt.Errorf("load token file: %v", err) 290 | } 291 | token = string(data) 292 | } 293 | 294 | transport := &http.Transport{ 295 | Proxy: http.ProxyFromEnvironment, 296 | DialContext: (&net.Dialer{ 297 | Timeout: 30 * time.Second, 298 | KeepAlive: 30 * time.Second, 299 | }).DialContext, 300 | TLSClientConfig: tlsConfig, 301 | MaxIdleConns: 100, 302 | IdleConnTimeout: 90 * time.Second, 303 | TLSHandshakeTimeout: 10 * time.Second, 304 | ExpectContinueTimeout: 1 * time.Second, 305 | } 306 | if err := http2.ConfigureTransport(transport); err != nil { 307 | return nil, err 308 | } 309 | 310 | client := &Client{ 311 | Endpoint: cluster.Server, 312 | Namespace: namespace, 313 | Client: &http.Client{ 314 | Transport: transport, 315 | }, 316 | } 317 | 318 | if token != "" { 319 | client.SetHeaders = func(h http.Header) error { 320 | h.Set("Authorization", "Bearer "+token) 321 | return nil 322 | } 323 | } 324 | if user.Username != "" && user.Password != "" { 325 | auth := user.Username + ":" + user.Password 326 | auth = "Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) 327 | client.SetHeaders = func(h http.Header) error { 328 | h.Set("Authorization", auth) 329 | return nil 330 | } 331 | } 332 | return client, nil 333 | } 334 | 335 | // APIError is an error from a unexpected status code. 336 | type APIError struct { 337 | // The status object returned by the Kubernetes API, 338 | Status *metav1.Status 339 | 340 | // Status code returned by the HTTP request. 341 | // 342 | // NOTE: For some reason the value set in Status.Code 343 | // doesn't correspond to the HTTP status code. Possibly 344 | // a bug? 345 | Code int 346 | } 347 | 348 | func (e *APIError) Error() string { 349 | if e.Status != nil && e.Status.Message != nil && e.Status.Status != nil { 350 | return fmt.Sprintf("kubernetes api: %s %d %s", *e.Status.Status, e.Code, *e.Status.Message) 351 | } 352 | return fmt.Sprintf("%#v", e) 353 | } 354 | 355 | func checkStatusCode(contentType string, statusCode int, body []byte) error { 356 | if statusCode/100 == 2 { 357 | return nil 358 | } 359 | 360 | return newAPIError(contentType, statusCode, body) 361 | } 362 | 363 | func newAPIError(contentType string, statusCode int, body []byte) error { 364 | status := new(metav1.Status) 365 | if err := unmarshal(body, contentType, status); err != nil { 366 | return fmt.Errorf("decode error status %d: %v", statusCode, err) 367 | } 368 | return &APIError{status, statusCode} 369 | } 370 | 371 | func (c *Client) client() *http.Client { 372 | if c.Client == nil { 373 | return http.DefaultClient 374 | } 375 | return c.Client 376 | } 377 | 378 | // Create creates a resource of a registered type. The API version and resource 379 | // type is determined by the type of the req argument. The result is unmarshaled 380 | // into req. 381 | // 382 | // configMap := corev1.ConfigMap{ 383 | // Metadata: &metav1.ObjectMeta{ 384 | // Name: k8s.String("my-configmap"), 385 | // Namespace: k8s.String("my-namespace"), 386 | // }, 387 | // Data: map[string]string{ 388 | // "my-key": "my-val", 389 | // }, 390 | // } 391 | // if err := client.Create(ctx, &configMap); err != nil { 392 | // // handle error 393 | // } 394 | // // resource is updated with response of create request 395 | // fmt.Println(configMap.Metadata.GetCreationTimestamp()) 396 | // 397 | func (c *Client) Create(ctx context.Context, req Resource, options ...Option) error { 398 | url, err := resourceURL(c.Endpoint, req, false, options...) 399 | if err != nil { 400 | return err 401 | } 402 | return c.do(ctx, "POST", url, req, req) 403 | } 404 | 405 | func (c *Client) Delete(ctx context.Context, req Resource, options ...Option) error { 406 | url, err := resourceURL(c.Endpoint, req, true, options...) 407 | if err != nil { 408 | return err 409 | } 410 | o := &deleteOptions{ 411 | Kind: "DeleteOptions", 412 | APIVersion: "v1", 413 | PropagationPolicy: "Background", 414 | } 415 | for _, option := range options { 416 | option.updateDelete(req, o) 417 | } 418 | 419 | return c.do(ctx, "DELETE", url, o, nil) 420 | } 421 | 422 | func (c *Client) Update(ctx context.Context, req Resource, options ...Option) error { 423 | url, err := resourceURL(c.Endpoint, req, true, options...) 424 | if err != nil { 425 | return err 426 | } 427 | return c.do(ctx, "PUT", url, req, req) 428 | } 429 | 430 | func (c *Client) Get(ctx context.Context, namespace, name string, resp Resource, options ...Option) error { 431 | url, err := resourceGetURL(c.Endpoint, namespace, name, resp, options...) 432 | if err != nil { 433 | return err 434 | } 435 | return c.do(ctx, "GET", url, nil, resp) 436 | } 437 | 438 | func (c *Client) List(ctx context.Context, namespace string, resp ResourceList, options ...Option) error { 439 | url, err := resourceListURL(c.Endpoint, namespace, resp, options...) 440 | if err != nil { 441 | return err 442 | } 443 | return c.do(ctx, "GET", url, nil, resp) 444 | } 445 | 446 | func (c *Client) do(ctx context.Context, verb, url string, req, resp interface{}) error { 447 | var ( 448 | contentType string 449 | body io.Reader 450 | ) 451 | if req != nil { 452 | ct, data, err := marshal(req) 453 | if err != nil { 454 | return fmt.Errorf("encoding object: %v", err) 455 | } 456 | contentType = ct 457 | body = bytes.NewReader(data) 458 | } 459 | r, err := http.NewRequest(verb, url, body) 460 | if err != nil { 461 | return fmt.Errorf("new request: %v", err) 462 | } 463 | if contentType != "" { 464 | r.Header.Set("Content-Type", contentType) 465 | r.Header.Set("Accept", contentType) 466 | } else if resp != nil { 467 | r.Header.Set("Accept", contentTypeFor(resp)) 468 | } 469 | if c.SetHeaders != nil { 470 | c.SetHeaders(r.Header) 471 | } 472 | 473 | re, err := c.client().Do(r) 474 | if err != nil { 475 | return fmt.Errorf("performing request: %v", err) 476 | } 477 | defer re.Body.Close() 478 | 479 | respBody, err := ioutil.ReadAll(re.Body) 480 | if err != nil { 481 | return fmt.Errorf("read body: %v", err) 482 | } 483 | 484 | respCT := re.Header.Get("Content-Type") 485 | if err := checkStatusCode(respCT, re.StatusCode, respBody); err != nil { 486 | return err 487 | } 488 | if resp != nil { 489 | if err := unmarshal(respBody, respCT, resp); err != nil { 490 | return fmt.Errorf("decode response: %v", err) 491 | } 492 | } 493 | return nil 494 | } 495 | -------------------------------------------------------------------------------- /client_test.go: -------------------------------------------------------------------------------- 1 | package k8s_test 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "math/rand" 8 | "os" 9 | "os/exec" 10 | "reflect" 11 | "strconv" 12 | "testing" 13 | "time" 14 | 15 | "github.com/ericchiang/k8s" 16 | corev1 "github.com/ericchiang/k8s/apis/core/v1" 17 | metav1 "github.com/ericchiang/k8s/apis/meta/v1" 18 | ) 19 | 20 | const skipMsg = ` 21 | warning: this package's test run using the default context of your "kubectl" command, 22 | and will create resources on your cluster (mostly configmaps). 23 | 24 | If you wish to continue set the following environment variable: 25 | 26 | export K8S_CLIENT_TEST=1 27 | 28 | To suppress this message, set: 29 | 30 | export K8S_CLIENT_TEST=0 31 | ` 32 | 33 | func newTestClient(t *testing.T) *k8s.Client { 34 | if os.Getenv("K8S_CLIENT_TEST") == "0" { 35 | t.Skip("") 36 | } 37 | if os.Getenv("K8S_CLIENT_TEST") != "1" { 38 | t.Skip(skipMsg) 39 | } 40 | 41 | cmd := exec.Command("kubectl", "config", "view", "-o", "json") 42 | out, err := cmd.CombinedOutput() 43 | if err != nil { 44 | t.Fatalf("'kubectl config view -o json': %v %s", err, out) 45 | } 46 | 47 | config := new(k8s.Config) 48 | if err := json.Unmarshal(out, config); err != nil { 49 | t.Fatalf("parse kubeconfig: %v '%s'", err, out) 50 | } 51 | client, err := k8s.NewClient(config) 52 | if err != nil { 53 | t.Fatalf("new client: %v", err) 54 | } 55 | return client 56 | } 57 | 58 | var letters = []rune("abcdefghijklmnopqrstuvwxyz") 59 | 60 | func withNamespace(t *testing.T, test func(client *k8s.Client, namespace string)) { 61 | client := newTestClient(t) 62 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 63 | b := make([]rune, 8) 64 | for i := range b { 65 | b[i] = letters[r.Intn(len(letters))] 66 | } 67 | name := "k8s-test-" + string(b) 68 | namespace := corev1.Namespace{ 69 | Metadata: &metav1.ObjectMeta{ 70 | Name: &name, 71 | }, 72 | } 73 | if err := client.Create(context.TODO(), &namespace); err != nil { 74 | t.Fatalf("create namespace: %v", err) 75 | } 76 | defer func() { 77 | if err := client.Delete(context.TODO(), &namespace); err != nil { 78 | t.Fatalf("delete namespace: %v", err) 79 | } 80 | }() 81 | 82 | test(client, name) 83 | } 84 | 85 | func TestNewTestClient(t *testing.T) { 86 | newTestClient(t) 87 | } 88 | 89 | func TestListNodes(t *testing.T) { 90 | client := newTestClient(t) 91 | var nodes corev1.NodeList 92 | if err := client.List(context.TODO(), "", &nodes); err != nil { 93 | t.Fatal(err) 94 | } 95 | if len(nodes.Items) == 0 { 96 | t.Skip("no nodes in cluster") 97 | } 98 | 99 | for _, node := range nodes.Items { 100 | if node.Metadata.Annotations == nil { 101 | node.Metadata.Annotations = map[string]string{} 102 | } 103 | node.Metadata.Annotations["foo"] = "bar" 104 | if err := client.Update(context.TODO(), node); err != nil { 105 | t.Fatal(err) 106 | } 107 | delete(node.Metadata.Annotations, "foo") 108 | if err := client.Update(context.TODO(), node); err != nil { 109 | t.Fatal(err) 110 | } 111 | } 112 | } 113 | 114 | func TestWithNamespace(t *testing.T) { 115 | withNamespace(t, func(client *k8s.Client, namespace string) {}) 116 | } 117 | 118 | func TestUpdateNamespaceStatus(t *testing.T) { 119 | withNamespace(t, func(client *k8s.Client, namespace string) { 120 | var ns corev1.Namespace 121 | if err := client.Get(context.TODO(), "", namespace, &ns); err != nil { 122 | t.Errorf("get namespace: %v", err) 123 | return 124 | } 125 | 126 | if err := client.Update(context.TODO(), &ns, k8s.Subresource("status")); err != nil { 127 | t.Errorf("update namespace status subresource: %v", err) 128 | } 129 | 130 | if err := client.Update(context.TODO(), &ns, k8s.Subresource("idontexist")); err == nil { 131 | t.Errorf("updated invalid subresource") 132 | } 133 | }) 134 | } 135 | 136 | func TestCreateConfigMap(t *testing.T) { 137 | withNamespace(t, func(client *k8s.Client, namespace string) { 138 | cm := &corev1.ConfigMap{ 139 | Metadata: &metav1.ObjectMeta{ 140 | Name: k8s.String("my-configmap"), 141 | Namespace: &namespace, 142 | }, 143 | } 144 | if err := client.Create(context.TODO(), cm); err != nil { 145 | t.Errorf("create configmap: %v", err) 146 | return 147 | } 148 | got := new(corev1.ConfigMap) 149 | if err := client.Get(context.TODO(), namespace, *cm.Metadata.Name, got); err != nil { 150 | t.Errorf("get configmap: %v", err) 151 | return 152 | } 153 | if !reflect.DeepEqual(cm, got) { 154 | t.Errorf("expected configmap %#v, got=%#v", cm, got) 155 | } 156 | 157 | if err := client.Delete(context.TODO(), cm); err != nil { 158 | t.Errorf("delete configmap: %v", err) 159 | return 160 | } 161 | }) 162 | } 163 | 164 | func TestListConfigMap(t *testing.T) { 165 | withNamespace(t, func(client *k8s.Client, namespace string) { 166 | for i := 0; i < 5; i++ { 167 | cm := &corev1.ConfigMap{ 168 | Metadata: &metav1.ObjectMeta{ 169 | Name: k8s.String(fmt.Sprintf("my-configmap-%d", i)), 170 | Namespace: &namespace, 171 | }, 172 | } 173 | if err := client.Create(context.TODO(), cm); err != nil { 174 | t.Errorf("create configmap: %v", err) 175 | return 176 | } 177 | } 178 | 179 | var configMapList corev1.ConfigMapList 180 | if err := client.List(context.TODO(), namespace, &configMapList); err != nil { 181 | t.Errorf("list configmaps: %v", err) 182 | return 183 | } 184 | 185 | if n := len(configMapList.Items); n != 5 { 186 | t.Errorf("expected 5 configmaps, got %d", n) 187 | } 188 | }) 189 | } 190 | 191 | func TestDefaultNamespace(t *testing.T) { 192 | c := &k8s.Config{ 193 | Clusters: []k8s.NamedCluster{ 194 | { 195 | Name: "local", 196 | Cluster: k8s.Cluster{ 197 | Server: "http://localhost:8080", 198 | }, 199 | }, 200 | }, 201 | AuthInfos: []k8s.NamedAuthInfo{ 202 | { 203 | Name: "local", 204 | }, 205 | }, 206 | } 207 | cli, err := k8s.NewClient(c) 208 | if err != nil { 209 | t.Fatal(err) 210 | } 211 | if cli.Namespace != "default" { 212 | t.Errorf("expected namespace=%q got=%q", "default", cli.Namespace) 213 | } 214 | } 215 | 216 | func TestDelete(t *testing.T) { 217 | t.Skip("this test requires a controller manager to run, see issue #95") 218 | 219 | withNamespace(t, func(client *k8s.Client, namespace string) { 220 | cm := &corev1.ConfigMap{ 221 | Metadata: &metav1.ObjectMeta{ 222 | Name: k8s.String("my-configmap"), 223 | Namespace: &namespace, 224 | }, 225 | } 226 | if err := client.Create(context.TODO(), cm); err != nil { 227 | t.Errorf("create configmap: %v", err) 228 | return 229 | } 230 | 231 | cm2 := &corev1.ConfigMap{ 232 | Metadata: &metav1.ObjectMeta{ 233 | Name: k8s.String("my-configmap-2"), 234 | Namespace: &namespace, 235 | OwnerReferences: []*metav1.OwnerReference{ 236 | { 237 | ApiVersion: k8s.String("v1"), 238 | Kind: k8s.String("ConfigMap"), 239 | Name: cm.Metadata.Name, 240 | Uid: cm.Metadata.Uid, 241 | BlockOwnerDeletion: k8s.Bool(true), 242 | }, 243 | }, 244 | }, 245 | } 246 | if err := client.Create(context.TODO(), cm2); err != nil { 247 | t.Errorf("create configmap: %v", err) 248 | return 249 | } 250 | 251 | if err := client.Delete(context.TODO(), cm, k8s.DeletePropagationForeground()); err != nil { 252 | t.Fatalf("delete configmap: %v", err) 253 | } 254 | 255 | var err error 256 | for i := 0; i < 50; i++ { 257 | err = func() error { 258 | err := client.Get(context.TODO(), namespace, *cm2.Metadata.Name, cm2) 259 | if err == nil { 260 | return fmt.Errorf("expected 404 error") 261 | } 262 | apiErr, ok := err.(*k8s.APIError) 263 | if !ok { 264 | return fmt.Errorf("error was not of type APIError: %T %v", err, err) 265 | } 266 | if apiErr.Code != 404 { 267 | return fmt.Errorf("expected 404 error code, got %d", apiErr.Code) 268 | } 269 | return nil 270 | }() 271 | if err == nil { 272 | break 273 | } 274 | time.Sleep(100 * time.Millisecond) 275 | } 276 | if err != nil { 277 | t.Errorf("getting parent configmap: %v", err) 278 | } 279 | }) 280 | } 281 | 282 | func TestDeleteOrphan(t *testing.T) { 283 | withNamespace(t, func(client *k8s.Client, namespace string) { 284 | cm := &corev1.ConfigMap{ 285 | Metadata: &metav1.ObjectMeta{ 286 | Name: k8s.String("my-configmap"), 287 | Namespace: &namespace, 288 | }, 289 | } 290 | if err := client.Create(context.TODO(), cm); err != nil { 291 | t.Errorf("create configmap: %v", err) 292 | return 293 | } 294 | 295 | cm2 := &corev1.ConfigMap{ 296 | Metadata: &metav1.ObjectMeta{ 297 | Name: k8s.String("my-configmap-2"), 298 | Namespace: &namespace, 299 | OwnerReferences: []*metav1.OwnerReference{ 300 | { 301 | ApiVersion: k8s.String("v1"), 302 | Kind: k8s.String("ConfigMap"), 303 | Name: cm.Metadata.Name, 304 | Uid: cm.Metadata.Uid, 305 | }, 306 | }, 307 | }, 308 | } 309 | if err := client.Create(context.TODO(), cm2); err != nil { 310 | t.Errorf("create configmap: %v", err) 311 | return 312 | } 313 | 314 | if err := client.Delete(context.TODO(), cm, k8s.DeletePropagationOrphan()); err != nil { 315 | t.Fatalf("delete configmap: %v", err) 316 | } 317 | 318 | // We're attempting to affirm a negative, that this won't eventually be deleted. 319 | // Since there's no explicit even for us to wait for 320 | time.Sleep(time.Second) 321 | 322 | err := client.Get(context.TODO(), namespace, *cm2.Metadata.Name, cm2) 323 | if err != nil { 324 | t.Errorf("failed to get configmap: %v", err) 325 | } 326 | }) 327 | } 328 | 329 | func Test404(t *testing.T) { 330 | withNamespace(t, func(client *k8s.Client, namespace string) { 331 | var configMap corev1.ConfigMap 332 | err := client.Get(context.TODO(), namespace, "i-dont-exist", &configMap) 333 | if err == nil { 334 | t.Errorf("expected 404 error") 335 | return 336 | } 337 | apiErr, ok := err.(*k8s.APIError) 338 | if !ok { 339 | t.Errorf("error was not of type APIError: %T %v", err, err) 340 | return 341 | } 342 | if apiErr.Code != 404 { 343 | t.Errorf("expected 404 error code, got %d", apiErr.Code) 344 | } 345 | }) 346 | } 347 | 348 | func Test404JSON(t *testing.T) { 349 | withNamespace(t, func(client *k8s.Client, namespace string) { 350 | // Use a JSON object to ensure JSON payload errors deserialize correctly. 351 | var configMap configMapJSON 352 | err := client.Get(context.TODO(), namespace, "i-dont-exist", &configMap) 353 | if err == nil { 354 | t.Errorf("expected 404 error") 355 | return 356 | } 357 | apiErr, ok := err.(*k8s.APIError) 358 | if !ok { 359 | t.Errorf("error was not of type APIError: %T %v", err, err) 360 | return 361 | } 362 | if apiErr.Code != 404 { 363 | t.Errorf("expected 404 error code, got %d", apiErr.Code) 364 | } 365 | }) 366 | } 367 | 368 | func TestLabelSelector(t *testing.T) { 369 | withNamespace(t, func(client *k8s.Client, namespace string) { 370 | for i := 0; i < 5; i++ { 371 | cm := &corev1.ConfigMap{ 372 | Metadata: &metav1.ObjectMeta{ 373 | Name: k8s.String(fmt.Sprintf("my-configmap-%d", i)), 374 | Namespace: &namespace, 375 | Labels: map[string]string{ 376 | "configmap": "true", 377 | "n": strconv.Itoa(i), 378 | "m": strconv.Itoa(i % 2), 379 | }, 380 | }, 381 | } 382 | if err := client.Create(context.TODO(), cm); err != nil { 383 | t.Errorf("create configmap: %v", err) 384 | return 385 | } 386 | } 387 | 388 | tests := []struct { 389 | setup func(l *k8s.LabelSelector) 390 | want int 391 | }{ 392 | { 393 | func(l *k8s.LabelSelector) { 394 | l.Eq("configmap", "true") 395 | }, 396 | 5, 397 | }, 398 | { 399 | func(l *k8s.LabelSelector) { 400 | l.Eq("configmap", "true") 401 | l.NotEq("n", "4") 402 | }, 403 | 4, 404 | }, 405 | { 406 | func(l *k8s.LabelSelector) { 407 | l.Eq("configmap", "false") 408 | }, 409 | 0, 410 | }, 411 | { 412 | func(l *k8s.LabelSelector) { 413 | l.Eq("configmap", "true") 414 | l.Eq("n", "4") 415 | }, 416 | 1, 417 | }, 418 | { 419 | func(l *k8s.LabelSelector) { 420 | l.Eq("configmap", "true") 421 | l.Eq("m", "0") 422 | }, 423 | 3, 424 | }, 425 | } 426 | 427 | for _, test := range tests { 428 | var configmaps corev1.ConfigMapList 429 | l := new(k8s.LabelSelector) 430 | test.setup(l) 431 | ctx := context.TODO() 432 | if err := client.List(ctx, namespace, &configmaps, l.Selector()); err != nil { 433 | t.Fatalf("list configmaps: %v", err) 434 | } 435 | if len(configmaps.Items) != test.want { 436 | t.Errorf("label selector %s expected %d items, got %d", 437 | l, test.want, len(configmaps.Items)) 438 | } 439 | } 440 | }) 441 | } 442 | -------------------------------------------------------------------------------- /codec.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | 9 | "github.com/ericchiang/k8s/runtime" 10 | "github.com/golang/protobuf/proto" 11 | ) 12 | 13 | const ( 14 | contentTypePB = "application/vnd.kubernetes.protobuf" 15 | contentTypeJSON = "application/json" 16 | ) 17 | 18 | func contentTypeFor(i interface{}) string { 19 | if _, ok := i.(proto.Message); ok { 20 | return contentTypePB 21 | } 22 | return contentTypeJSON 23 | } 24 | 25 | // marshal encodes an object and returns the content type of that resource 26 | // and the marshaled representation. 27 | // 28 | // marshal prefers protobuf encoding, but falls back to JSON. 29 | func marshal(i interface{}) (string, []byte, error) { 30 | if _, ok := i.(proto.Message); ok { 31 | data, err := marshalPB(i) 32 | return contentTypePB, data, err 33 | } 34 | data, err := json.Marshal(i) 35 | return contentTypeJSON, data, err 36 | } 37 | 38 | // unmarshal decoded an object given the content type of the encoded form. 39 | func unmarshal(data []byte, contentType string, i interface{}) error { 40 | msg, isPBMsg := i.(proto.Message) 41 | if contentType == contentTypePB && isPBMsg { 42 | if err := unmarshalPB(data, msg); err != nil { 43 | return fmt.Errorf("decode protobuf: %v", err) 44 | } 45 | return nil 46 | } 47 | if isPBMsg { 48 | // only decode into JSON of a protobuf message if the type 49 | // explicitly implements json.Unmarshaler 50 | if _, ok := i.(json.Unmarshaler); !ok { 51 | return fmt.Errorf("cannot decode json payload into protobuf object %T", i) 52 | } 53 | } 54 | if err := json.Unmarshal(data, i); err != nil { 55 | return fmt.Errorf("decode json: %v", err) 56 | } 57 | return nil 58 | } 59 | 60 | var magicBytes = []byte{0x6b, 0x38, 0x73, 0x00} 61 | 62 | func unmarshalPB(b []byte, msg proto.Message) error { 63 | if len(b) < len(magicBytes) { 64 | return errors.New("payload is not a kubernetes protobuf object") 65 | } 66 | if !bytes.Equal(b[:len(magicBytes)], magicBytes) { 67 | return errors.New("payload is not a kubernetes protobuf object") 68 | } 69 | 70 | u := new(runtime.Unknown) 71 | if err := u.Unmarshal(b[len(magicBytes):]); err != nil { 72 | return fmt.Errorf("unmarshal unknown: %v", err) 73 | } 74 | return proto.Unmarshal(u.Raw, msg) 75 | } 76 | 77 | func marshalPB(obj interface{}) ([]byte, error) { 78 | message, ok := obj.(proto.Message) 79 | if !ok { 80 | return nil, fmt.Errorf("expected obj of type proto.Message, got %T", obj) 81 | } 82 | payload, err := proto.Marshal(message) 83 | if err != nil { 84 | return nil, err 85 | } 86 | 87 | // The URL path informs the API server what the API group, version, and resource 88 | // of the object. We don't need to specify it here to talk to the API server. 89 | body, err := (&runtime.Unknown{Raw: payload}).Marshal() 90 | if err != nil { 91 | return nil, err 92 | } 93 | 94 | d := make([]byte, len(magicBytes)+len(body)) 95 | copy(d[:len(magicBytes)], magicBytes) 96 | copy(d[len(magicBytes):], body) 97 | return d, nil 98 | } 99 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 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 k8s 18 | 19 | import ( 20 | "github.com/ericchiang/k8s/runtime" 21 | ) 22 | 23 | // Where possible, json tags match the cli argument names. 24 | // Top level config objects and all values required for proper functioning are not "omitempty". Any truly optional piece of config is allowed to be omitted. 25 | 26 | // Config holds the information needed to build connect to remote kubernetes clusters as a given user 27 | type Config struct { 28 | // Legacy field from pkg/api/types.go TypeMeta. 29 | // TODO(jlowdermilk): remove this after eliminating downstream dependencies. 30 | // +optional 31 | Kind string `json:"kind,omitempty" yaml:"kind,omitempty"` 32 | // DEPRECATED: APIVersion is the preferred api version for communicating with the kubernetes cluster (v1, v2, etc). 33 | // Because a cluster can run multiple API groups and potentially multiple versions of each, it no longer makes sense to specify 34 | // a single value for the cluster version. 35 | // This field isn't really needed anyway, so we are deprecating it without replacement. 36 | // It will be ignored if it is present. 37 | // +optional 38 | APIVersion string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty"` 39 | // Preferences holds general information to be use for cli interactions 40 | Preferences Preferences `json:"preferences" yaml:"preferences"` 41 | // Clusters is a map of referencable names to cluster configs 42 | Clusters []NamedCluster `json:"clusters" yaml:"clusters"` 43 | // AuthInfos is a map of referencable names to user configs 44 | AuthInfos []NamedAuthInfo `json:"users" yaml:"users"` 45 | // Contexts is a map of referencable names to context configs 46 | Contexts []NamedContext `json:"contexts" yaml:"contexts"` 47 | // CurrentContext is the name of the context that you would like to use by default 48 | CurrentContext string `json:"current-context" yaml:"current-context"` 49 | // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields 50 | // +optional 51 | Extensions []NamedExtension `json:"extensions,omitempty" yaml:"extensions,omitempty"` 52 | } 53 | 54 | type Preferences struct { 55 | // +optional 56 | Colors bool `json:"colors,omitempty" yaml:"colors,omitempty"` 57 | // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields 58 | // +optional 59 | Extensions []NamedExtension `json:"extensions,omitempty" yaml:"extensions,omitempty"` 60 | } 61 | 62 | // Cluster contains information about how to communicate with a kubernetes cluster 63 | type Cluster struct { 64 | // Server is the address of the kubernetes cluster (https://hostname:port). 65 | Server string `json:"server" yaml:"server"` 66 | // APIVersion is the preferred api version for communicating with the kubernetes cluster (v1, v2, etc). 67 | // +optional 68 | APIVersion string `json:"api-version,omitempty" yaml:"api-version,omitempty"` 69 | // InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure. 70 | // +optional 71 | InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty" yaml:"insecure-skip-tls-verify,omitempty"` 72 | // CertificateAuthority is the path to a cert file for the certificate authority. 73 | // +optional 74 | CertificateAuthority string `json:"certificate-authority,omitempty" yaml:"certificate-authority,omitempty"` 75 | // CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority 76 | // +optional 77 | CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty" yaml:"certificate-authority-data,omitempty"` 78 | // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields 79 | // +optional 80 | Extensions []NamedExtension `json:"extensions,omitempty" yaml:"extensions,omitempty"` 81 | } 82 | 83 | // AuthInfo contains information that describes identity information. This is use to tell the kubernetes cluster who you are. 84 | type AuthInfo struct { 85 | // ClientCertificate is the path to a client cert file for TLS. 86 | // +optional 87 | ClientCertificate string `json:"client-certificate,omitempty" yaml:"client-certificate,omitempty"` 88 | // ClientCertificateData contains PEM-encoded data from a client cert file for TLS. Overrides ClientCertificate 89 | // +optional 90 | ClientCertificateData []byte `json:"client-certificate-data,omitempty" yaml:"client-certificate-data,omitempty"` 91 | // ClientKey is the path to a client key file for TLS. 92 | // +optional 93 | ClientKey string `json:"client-key,omitempty" yaml:"client-key,omitempty"` 94 | // ClientKeyData contains PEM-encoded data from a client key file for TLS. Overrides ClientKey 95 | // +optional 96 | ClientKeyData []byte `json:"client-key-data,omitempty" yaml:"client-key-data,omitempty"` 97 | // Token is the bearer token for authentication to the kubernetes cluster. 98 | // +optional 99 | Token string `json:"token,omitempty" yaml:"token,omitempty"` 100 | // TokenFile is a pointer to a file that contains a bearer token (as described above). If both Token and TokenFile are present, Token takes precedence. 101 | // +optional 102 | TokenFile string `json:"tokenFile,omitempty" yaml:"tokenFile,omitempty"` 103 | // Impersonate is the username to imperonate. The name matches the flag. 104 | // +optional 105 | Impersonate string `json:"as,omitempty" yaml:"as,omitempty"` 106 | // Username is the username for basic authentication to the kubernetes cluster. 107 | // +optional 108 | Username string `json:"username,omitempty" yaml:"username,omitempty"` 109 | // Password is the password for basic authentication to the kubernetes cluster. 110 | // +optional 111 | Password string `json:"password,omitempty" yaml:"password,omitempty"` 112 | // AuthProvider specifies a custom authentication plugin for the kubernetes cluster. 113 | // +optional 114 | AuthProvider *AuthProviderConfig `json:"auth-provider,omitempty" yaml:"auth-provider,omitempty"` 115 | // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields 116 | // +optional 117 | Extensions []NamedExtension `json:"extensions,omitempty" yaml:"extensions,omitempty"` 118 | } 119 | 120 | // Context is a tuple of references to a cluster (how do I communicate with a kubernetes cluster), a user (how do I identify myself), and a namespace (what subset of resources do I want to work with) 121 | type Context struct { 122 | // Cluster is the name of the cluster for this context 123 | Cluster string `json:"cluster" yaml:"cluster"` 124 | // AuthInfo is the name of the authInfo for this context 125 | AuthInfo string `json:"user" yaml:"user"` 126 | // Namespace is the default namespace to use on unspecified requests 127 | // +optional 128 | Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` 129 | // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields 130 | // +optional 131 | Extensions []NamedExtension `json:"extensions,omitempty" yaml:"extensions,omitempty"` 132 | } 133 | 134 | // NamedCluster relates nicknames to cluster information 135 | type NamedCluster struct { 136 | // Name is the nickname for this Cluster 137 | Name string `json:"name" yaml:"name"` 138 | // Cluster holds the cluster information 139 | Cluster Cluster `json:"cluster" yaml:"cluster"` 140 | } 141 | 142 | // NamedContext relates nicknames to context information 143 | type NamedContext struct { 144 | // Name is the nickname for this Context 145 | Name string `json:"name" yaml:"name"` 146 | // Context holds the context information 147 | Context Context `json:"context" yaml:"context"` 148 | } 149 | 150 | // NamedAuthInfo relates nicknames to auth information 151 | type NamedAuthInfo struct { 152 | // Name is the nickname for this AuthInfo 153 | Name string `json:"name" yaml:"name"` 154 | // AuthInfo holds the auth information 155 | AuthInfo AuthInfo `json:"user" yaml:"user"` 156 | } 157 | 158 | // NamedExtension relates nicknames to extension information 159 | type NamedExtension struct { 160 | // Name is the nickname for this Extension 161 | Name string `json:"name" yaml:"name"` 162 | // Extension holds the extension information 163 | Extension runtime.RawExtension `json:"extension" yaml:"extension"` 164 | } 165 | 166 | // AuthProviderConfig holds the configuration for a specified auth provider. 167 | type AuthProviderConfig struct { 168 | Name string `json:"name" yaml:"name"` 169 | Config map[string]string `json:"config" yaml:"config"` 170 | } 171 | -------------------------------------------------------------------------------- /discovery.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "context" 5 | "path" 6 | 7 | metav1 "github.com/ericchiang/k8s/apis/meta/v1" 8 | ) 9 | 10 | type Version struct { 11 | Major string `json:"major"` 12 | Minor string `json:"minor"` 13 | GitVersion string `json:"gitVersion"` 14 | GitCommit string `json:"gitCommit"` 15 | GitTreeState string `json:"gitTreeState"` 16 | BuildDate string `json:"buildDate"` 17 | GoVersion string `json:"goVersion"` 18 | Compiler string `json:"compiler"` 19 | Platform string `json:"platform"` 20 | } 21 | 22 | // Discovery is a client used to determine the API version and supported 23 | // resources of the server. 24 | type Discovery struct { 25 | client *Client 26 | } 27 | 28 | func NewDiscoveryClient(c *Client) *Discovery { 29 | return &Discovery{c} 30 | } 31 | 32 | func (d *Discovery) get(ctx context.Context, path string, resp interface{}) error { 33 | return d.client.do(ctx, "GET", urlForPath(d.client.Endpoint, path), nil, resp) 34 | } 35 | 36 | func (d *Discovery) Version(ctx context.Context) (*Version, error) { 37 | var v Version 38 | if err := d.get(ctx, "version", &v); err != nil { 39 | return nil, err 40 | } 41 | return &v, nil 42 | } 43 | 44 | func (d *Discovery) APIGroups(ctx context.Context) (*metav1.APIGroupList, error) { 45 | var groups metav1.APIGroupList 46 | if err := d.get(ctx, "apis", &groups); err != nil { 47 | return nil, err 48 | } 49 | return &groups, nil 50 | } 51 | 52 | func (d *Discovery) APIGroup(ctx context.Context, name string) (*metav1.APIGroup, error) { 53 | var group metav1.APIGroup 54 | if err := d.get(ctx, path.Join("apis", name), &group); err != nil { 55 | return nil, err 56 | } 57 | return &group, nil 58 | } 59 | 60 | func (d *Discovery) APIResources(ctx context.Context, groupName, groupVersion string) (*metav1.APIResourceList, error) { 61 | var list metav1.APIResourceList 62 | if err := d.get(ctx, path.Join("apis", groupName, groupVersion), &list); err != nil { 63 | return nil, err 64 | } 65 | return &list, nil 66 | } 67 | -------------------------------------------------------------------------------- /discovery_test.go: -------------------------------------------------------------------------------- 1 | package k8s_test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/ericchiang/k8s" 8 | ) 9 | 10 | func TestDiscovery(t *testing.T) { 11 | client := k8s.NewDiscoveryClient(newTestClient(t)) 12 | ctx, cancel := context.WithCancel(context.Background()) 13 | defer cancel() 14 | 15 | if _, err := client.Version(ctx); err != nil { 16 | t.Errorf("list version: %v", err) 17 | } 18 | 19 | if _, err := client.APIGroups(ctx); err != nil { 20 | t.Errorf("list api groups: %v", err) 21 | } 22 | 23 | if _, err := client.APIGroup(ctx, "extensions"); err != nil { 24 | t.Errorf("list api group: %v", err) 25 | } 26 | 27 | if _, err := client.APIResources(ctx, "extensions", "v1beta1"); err != nil { 28 | t.Errorf("list api group resources: %v", err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/api-errors.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package configmaps 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "net/http" 9 | 10 | "github.com/ericchiang/k8s" 11 | "github.com/ericchiang/k8s/apis/core/v1" 12 | metav1 "github.com/ericchiang/k8s/apis/meta/v1" 13 | ) 14 | 15 | // createConfigMap creates a configmap in the client's default namespace 16 | // but does not return an error if a configmap of the same name already 17 | // exists. 18 | func createConfigMap(client *k8s.Client, name string, values map[string]string) error { 19 | cm := &v1.ConfigMap{ 20 | Metadata: &metav1.ObjectMeta{ 21 | Name: &name, 22 | Namespace: &client.Namespace, 23 | }, 24 | Data: values, 25 | } 26 | 27 | err := client.Create(context.Background(), cm) 28 | 29 | // If an HTTP error was returned by the API server, it will be of type 30 | // *k8s.APIError. This can be used to inspect the status code. 31 | if apiErr, ok := err.(*k8s.APIError); ok { 32 | // Resource already exists. Carry on. 33 | if apiErr.Code == http.StatusConflict { 34 | return nil 35 | } 36 | } 37 | return fmt.Errorf("create configmap: %v", err) 38 | } 39 | -------------------------------------------------------------------------------- /examples/create-resource.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package configmaps 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/ericchiang/k8s" 9 | "github.com/ericchiang/k8s/apis/core/v1" 10 | metav1 "github.com/ericchiang/k8s/apis/meta/v1" 11 | ) 12 | 13 | func createConfigMap(client *k8s.Client, name string, values map[string]string) error { 14 | cm := &v1.ConfigMap{ 15 | Metadata: &metav1.ObjectMeta{ 16 | Name: &name, 17 | Namespace: &client.Namespace, 18 | }, 19 | Data: values, 20 | } 21 | // Will return the created configmap as well. 22 | return client.Create(context.TODO(), cm) 23 | } 24 | -------------------------------------------------------------------------------- /examples/custom-resources.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package customresources 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | 9 | "github.com/ericchiang/k8s" 10 | metav1 "github.com/ericchiang/k8s/apis/meta/v1" 11 | ) 12 | 13 | func init() { 14 | k8s.Register("resource.example.com", "v1", "myresource", true, &MyResource{}) 15 | k8s.RegisterList("resource.example.com", "v1", "myresource", true, &MyResourceList{}) 16 | } 17 | 18 | type MyResource struct { 19 | Metadata *metav1.ObjectMeta `json:"metadata"` 20 | Foo string `json:"foo"` 21 | Bar int `json:"bar"` 22 | } 23 | 24 | func (m *MyResource) GetMetadata() *metav1.ObjectMeta { 25 | return m.Metadata 26 | } 27 | 28 | type MyResourceList struct { 29 | Metadata *metav1.ListMeta `json:"metadata"` 30 | Items []MyResource `json:"items"` 31 | } 32 | 33 | func (m *MyResourceList) GetMetadata() *metav1.ListMeta { 34 | return m.Metadata 35 | } 36 | 37 | func do(ctx context.Context, client *k8s.Client, namespace string) error { 38 | r := &MyResource{ 39 | Metadata: &metav1.ObjectMeta{ 40 | Name: k8s.String("my-custom-resource"), 41 | Namespace: &namespace, 42 | }, 43 | Foo: "hello, world!", 44 | Bar: 42, 45 | } 46 | if err := client.Create(ctx, r); err != nil { 47 | return fmt.Errorf("create: %v", err) 48 | } 49 | r.Bar = -8 50 | if err := client.Update(ctx, r); err != nil { 51 | return fmt.Errorf("update: %v", err) 52 | } 53 | if err := client.Delete(ctx, r); err != nil { 54 | return fmt.Errorf("delete: %v", err) 55 | } 56 | return nil 57 | } 58 | -------------------------------------------------------------------------------- /examples/in-cluster-client.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package nodes 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "log" 9 | 10 | "github.com/ericchiang/k8s" 11 | corev1 "github.com/ericchiang/k8s/apis/core/v1" 12 | ) 13 | 14 | func listNodes() { 15 | client, err := k8s.NewInClusterClient() 16 | if err != nil { 17 | log.Fatal(err) 18 | } 19 | 20 | var nodes corev1.NodeList 21 | if err := client.List(context.Background(), "", &nodes); err != nil { 22 | log.Fatal(err) 23 | } 24 | for _, node := range nodes.Items { 25 | fmt.Printf("name=%q schedulable=%t\n", *node.Metadata.Name, !*node.Spec.Unschedulable) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/out-of-cluster-client.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package kubeconfig 4 | 5 | import ( 6 | "fmt" 7 | "io/ioutil" 8 | 9 | "github.com/ericchiang/k8s" 10 | 11 | "github.com/ghodss/yaml" 12 | ) 13 | 14 | // loadClient parses a kubeconfig from a file and returns a Kubernetes 15 | // client. It does not support extensions or client auth providers. 16 | func loadClient(kubeconfigPath string) (*k8s.Client, error) { 17 | data, err := ioutil.ReadFile(kubeconfigPath) 18 | if err != nil { 19 | return nil, fmt.Errorf("read kubeconfig: %v", err) 20 | } 21 | 22 | // Unmarshal YAML into a Kubernetes config object. 23 | var config k8s.Config 24 | if err := yaml.Unmarshal(data, &config); err != nil { 25 | return nil, fmt.Errorf("unmarshal kubeconfig: %v", err) 26 | } 27 | return k8s.NewClient(&config) 28 | } 29 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ericchiang/k8s 2 | 3 | require ( 4 | github.com/golang/protobuf v1.2.0 5 | golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 6 | golang.org/x/text v0.3.0 // indirect 7 | ) 8 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 2 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 3 | golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnYDVpCAYBVuYst/L+fAY= 4 | golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 5 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 6 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 7 | -------------------------------------------------------------------------------- /labels.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | ) 7 | 8 | const ( 9 | qnameCharFmt string = "[A-Za-z0-9]" 10 | qnameExtCharFmt string = "[-A-Za-z0-9_./]" 11 | qualifiedNameFmt string = "(" + qnameCharFmt + qnameExtCharFmt + "*)?" + qnameCharFmt 12 | qualifiedNameMaxLength int = 63 13 | labelValueFmt string = "(" + qualifiedNameFmt + ")?" 14 | ) 15 | 16 | var labelValueRegexp = regexp.MustCompile("^" + labelValueFmt + "$") 17 | 18 | // LabelSelector represents a Kubernetes label selector. 19 | // 20 | // Any values that don't conform to Kubernetes label value restrictions 21 | // will be silently dropped. 22 | // 23 | // l := new(k8s.LabelSelector) 24 | // l.Eq("component", "frontend") 25 | // l.In("type", "prod", "staging") 26 | // 27 | type LabelSelector struct { 28 | stmts []string 29 | } 30 | 31 | func (l *LabelSelector) Selector() Option { 32 | return QueryParam("labelSelector", l.String()) 33 | } 34 | 35 | func (l *LabelSelector) String() string { 36 | return strings.Join(l.stmts, ",") 37 | } 38 | 39 | func validLabelValue(s string) bool { 40 | if len(s) > 63 || len(s) == 0 { 41 | return false 42 | } 43 | return labelValueRegexp.MatchString(s) 44 | } 45 | 46 | // Eq selects labels which have the key and the key has the provide value. 47 | func (l *LabelSelector) Eq(key, val string) { 48 | if !validLabelValue(key) || !validLabelValue(val) { 49 | return 50 | } 51 | l.stmts = append(l.stmts, key+"="+val) 52 | } 53 | 54 | // NotEq selects labels where the key is present and has a different value 55 | // than the value provided. 56 | func (l *LabelSelector) NotEq(key, val string) { 57 | if !validLabelValue(key) || !validLabelValue(val) { 58 | return 59 | } 60 | l.stmts = append(l.stmts, key+"!="+val) 61 | } 62 | 63 | // In selects labels which have the key and the key has one of the provided values. 64 | func (l *LabelSelector) In(key string, vals ...string) { 65 | if !validLabelValue(key) || len(vals) == 0 { 66 | return 67 | } 68 | for _, val := range vals { 69 | if !validLabelValue(val) { 70 | return 71 | } 72 | } 73 | l.stmts = append(l.stmts, key+" in ("+strings.Join(vals, ", ")+")") 74 | } 75 | 76 | // NotIn selects labels which have the key and the key is not one of the provided values. 77 | func (l *LabelSelector) NotIn(key string, vals ...string) { 78 | if !validLabelValue(key) || len(vals) == 0 { 79 | return 80 | } 81 | for _, val := range vals { 82 | if !validLabelValue(val) { 83 | return 84 | } 85 | } 86 | l.stmts = append(l.stmts, key+" notin ("+strings.Join(vals, ", ")+")") 87 | } 88 | -------------------------------------------------------------------------------- /labels_test.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import "testing" 4 | 5 | func TestLabelSelector(t *testing.T) { 6 | tests := []struct { 7 | f func(l *LabelSelector) 8 | want string 9 | }{ 10 | { 11 | f: func(l *LabelSelector) { 12 | l.Eq("component", "frontend") 13 | }, 14 | want: "component=frontend", 15 | }, 16 | { 17 | f: func(l *LabelSelector) { 18 | l.Eq("kubernetes.io/role", "master") 19 | }, 20 | want: "kubernetes.io/role=master", 21 | }, 22 | { 23 | f: func(l *LabelSelector) { 24 | l.In("type", "prod", "staging") 25 | l.Eq("component", "frontend") 26 | }, 27 | want: "type in (prod, staging),component=frontend", 28 | }, 29 | { 30 | f: func(l *LabelSelector) { 31 | l.NotIn("type", "prod", "staging") 32 | l.NotEq("component", "frontend") 33 | }, 34 | want: "type notin (prod, staging),component!=frontend", 35 | }, 36 | { 37 | f: func(l *LabelSelector) { 38 | l.Eq("foo", "I am not a valid label value") 39 | }, 40 | want: "", 41 | }, 42 | } 43 | 44 | for i, test := range tests { 45 | l := new(LabelSelector) 46 | test.f(l) 47 | got := l.String() 48 | if test.want != got { 49 | t.Errorf("case %d: want=%q, got=%q", i, test.want, got) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /resource.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net/url" 7 | "path" 8 | "reflect" 9 | "strconv" 10 | "strings" 11 | "time" 12 | 13 | metav1 "github.com/ericchiang/k8s/apis/meta/v1" 14 | ) 15 | 16 | // Option represents optional call parameters, such as label selectors. 17 | type Option interface { 18 | updateURL(base string, v url.Values) string 19 | updateDelete(r Resource, d *deleteOptions) 20 | } 21 | 22 | type optionFunc func(base string, v url.Values) string 23 | 24 | func (f optionFunc) updateDelete(r Resource, d *deleteOptions) {} 25 | func (f optionFunc) updateURL(base string, v url.Values) string { return f(base, v) } 26 | 27 | type deleteOptions struct { 28 | Kind string `json:"kind"` 29 | APIVersion string `json:"apiVersion"` 30 | 31 | GracePeriod *int64 `json:"gracePeriodSeconds,omitempty"` 32 | Preconditions struct { 33 | UID string `json:"uid,omitempty"` 34 | } `json:"preconditions"` 35 | PropagationPolicy string `json:"propagationPolicy"` 36 | } 37 | 38 | // QueryParam can be used to manually set a URL query parameter by name. 39 | func QueryParam(name, value string) Option { 40 | return optionFunc(func(base string, v url.Values) string { 41 | v.Set(name, value) 42 | return base 43 | }) 44 | } 45 | 46 | type deleteOptionFunc func(r Resource, d *deleteOptions) 47 | 48 | func (f deleteOptionFunc) updateDelete(r Resource, d *deleteOptions) { f(r, d) } 49 | func (f deleteOptionFunc) updateURL(base string, v url.Values) string { return base } 50 | 51 | func DeleteAtomic() Option { 52 | return deleteOptionFunc(func(r Resource, d *deleteOptions) { 53 | d.Preconditions.UID = *r.GetMetadata().Uid 54 | }) 55 | } 56 | 57 | // DeletePropagationOrphan orphans the dependent resources during a delete. 58 | func DeletePropagationOrphan() Option { 59 | return deleteOptionFunc(func(r Resource, d *deleteOptions) { 60 | d.PropagationPolicy = "Orphan" 61 | }) 62 | } 63 | 64 | // DeletePropagationBackground deletes the resources and causes the garbage 65 | // collector to delete dependent resources in the background. 66 | func DeletePropagationBackground() Option { 67 | return deleteOptionFunc(func(r Resource, d *deleteOptions) { 68 | d.PropagationPolicy = "Background" 69 | }) 70 | } 71 | 72 | // DeletePropagationForeground deletes the resources and causes the garbage 73 | // collector to delete dependent resources and wait for all dependents whose 74 | // ownerReference.blockOwnerDeletion=true. API sever will put the "foregroundDeletion" 75 | // finalizer on the object, and sets its deletionTimestamp. This policy is 76 | // cascading, i.e., the dependents will be deleted with Foreground. 77 | func DeletePropagationForeground() Option { 78 | return deleteOptionFunc(func(r Resource, d *deleteOptions) { 79 | d.PropagationPolicy = "Foreground" 80 | }) 81 | } 82 | 83 | func DeleteGracePeriod(d time.Duration) Option { 84 | seconds := int64(d / time.Second) 85 | return deleteOptionFunc(func(r Resource, d *deleteOptions) { 86 | d.GracePeriod = &seconds 87 | }) 88 | } 89 | 90 | // ResourceVersion causes watch operations to only show changes since 91 | // a particular version of a resource. 92 | func ResourceVersion(resourceVersion string) Option { 93 | return QueryParam("resourceVersion", resourceVersion) 94 | } 95 | 96 | // Timeout declares the timeout for list and watch operations. Timeout 97 | // is only accurate to the second. 98 | func Timeout(d time.Duration) Option { 99 | return QueryParam( 100 | "timeoutSeconds", 101 | strconv.FormatInt(int64(d/time.Second), 10), 102 | ) 103 | } 104 | 105 | // Subresource is a way to interact with a part of an API object without needing 106 | // permissions on the entire resource. For example, a node isn't able to modify 107 | // a pod object, but can update the "pods/status" subresource. 108 | // 109 | // Common subresources are "status" and "scale". 110 | // 111 | // See https://kubernetes.io/docs/reference/api-concepts/ 112 | func Subresource(name string) Option { 113 | return optionFunc(func(base string, v url.Values) string { 114 | return base + "/" + name 115 | }) 116 | } 117 | 118 | type resourceType struct { 119 | apiGroup string 120 | apiVersion string 121 | name string 122 | namespaced bool 123 | } 124 | 125 | var ( 126 | resources = map[reflect.Type]resourceType{} 127 | resourceLists = map[reflect.Type]resourceType{} 128 | ) 129 | 130 | // Resource is a Kubernetes resource, such as a Node or Pod. 131 | type Resource interface { 132 | GetMetadata() *metav1.ObjectMeta 133 | } 134 | 135 | // Resource is list of common Kubernetes resources, such as a NodeList or 136 | // PodList. 137 | type ResourceList interface { 138 | GetMetadata() *metav1.ListMeta 139 | } 140 | 141 | func Register(apiGroup, apiVersion, name string, namespaced bool, r Resource) { 142 | rt := reflect.TypeOf(r) 143 | if _, ok := resources[rt]; ok { 144 | panic(fmt.Sprintf("resource registered twice %T", r)) 145 | } 146 | resources[rt] = resourceType{apiGroup, apiVersion, name, namespaced} 147 | } 148 | 149 | func RegisterList(apiGroup, apiVersion, name string, namespaced bool, l ResourceList) { 150 | rt := reflect.TypeOf(l) 151 | if _, ok := resources[rt]; ok { 152 | panic(fmt.Sprintf("resource registered twice %T", l)) 153 | } 154 | resourceLists[rt] = resourceType{apiGroup, apiVersion, name, namespaced} 155 | } 156 | 157 | func urlFor(endpoint, apiGroup, apiVersion, namespace, resource, name string, options ...Option) string { 158 | basePath := "apis/" 159 | if apiGroup == "" { 160 | basePath = "api/" 161 | } 162 | 163 | var p string 164 | if namespace != "" { 165 | p = path.Join(basePath, apiGroup, apiVersion, "namespaces", namespace, resource, name) 166 | } else { 167 | p = path.Join(basePath, apiGroup, apiVersion, resource, name) 168 | } 169 | e := "" 170 | if strings.HasSuffix(endpoint, "/") { 171 | e = endpoint + p 172 | } else { 173 | e = endpoint + "/" + p 174 | } 175 | if len(options) == 0 { 176 | return e 177 | } 178 | 179 | v := url.Values{} 180 | for _, option := range options { 181 | e = option.updateURL(e, v) 182 | } 183 | if len(v) == 0 { 184 | return e 185 | } 186 | return e + "?" + v.Encode() 187 | } 188 | 189 | func urlForPath(endpoint, path string) string { 190 | if strings.HasPrefix(path, "/") { 191 | path = path[1:] 192 | } 193 | if strings.HasSuffix(endpoint, "/") { 194 | return endpoint + path 195 | } 196 | return endpoint + "/" + path 197 | } 198 | 199 | func resourceURL(endpoint string, r Resource, withName bool, options ...Option) (string, error) { 200 | t, ok := resources[reflect.TypeOf(r)] 201 | if !ok { 202 | return "", fmt.Errorf("unregistered type %T", r) 203 | } 204 | meta := r.GetMetadata() 205 | if meta == nil { 206 | return "", errors.New("resource has no object meta") 207 | } 208 | switch { 209 | case t.namespaced && (meta.Namespace == nil || *meta.Namespace == ""): 210 | return "", errors.New("no resource namespace provided") 211 | case !t.namespaced && (meta.Namespace != nil && *meta.Namespace != ""): 212 | return "", errors.New("resource not namespaced") 213 | case withName && (meta.Name == nil || *meta.Name == ""): 214 | return "", errors.New("no resource name provided") 215 | } 216 | name := "" 217 | if withName { 218 | name = *meta.Name 219 | } 220 | namespace := "" 221 | if t.namespaced { 222 | namespace = *meta.Namespace 223 | } 224 | 225 | return urlFor(endpoint, t.apiGroup, t.apiVersion, namespace, t.name, name, options...), nil 226 | } 227 | 228 | func resourceGetURL(endpoint, namespace, name string, r Resource, options ...Option) (string, error) { 229 | t, ok := resources[reflect.TypeOf(r)] 230 | if !ok { 231 | return "", fmt.Errorf("unregistered type %T", r) 232 | } 233 | 234 | if !t.namespaced && namespace != "" { 235 | return "", fmt.Errorf("type not namespaced") 236 | } 237 | if t.namespaced && namespace == "" { 238 | return "", fmt.Errorf("no namespace provided") 239 | } 240 | 241 | return urlFor(endpoint, t.apiGroup, t.apiVersion, namespace, t.name, name, options...), nil 242 | } 243 | 244 | func resourceListURL(endpoint, namespace string, r ResourceList, options ...Option) (string, error) { 245 | t, ok := resourceLists[reflect.TypeOf(r)] 246 | if !ok { 247 | return "", fmt.Errorf("unregistered type %T", r) 248 | } 249 | 250 | if !t.namespaced && namespace != "" { 251 | return "", fmt.Errorf("type not namespaced") 252 | } 253 | 254 | return urlFor(endpoint, t.apiGroup, t.apiVersion, namespace, t.name, "", options...), nil 255 | } 256 | 257 | func resourceWatchURL(endpoint, namespace string, r Resource, options ...Option) (string, error) { 258 | t, ok := resources[reflect.TypeOf(r)] 259 | if !ok { 260 | return "", fmt.Errorf("unregistered type %T", r) 261 | } 262 | 263 | if !t.namespaced && namespace != "" { 264 | return "", fmt.Errorf("type not namespaced") 265 | } 266 | 267 | url := urlFor(endpoint, t.apiGroup, t.apiVersion, namespace, t.name, "", options...) 268 | if strings.Contains(url, "?") { 269 | url = url + "&watch=true" 270 | } else { 271 | url = url + "?watch=true" 272 | } 273 | return url, nil 274 | } 275 | -------------------------------------------------------------------------------- /resource_test.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | metav1 "github.com/ericchiang/k8s/apis/meta/v1" 8 | ) 9 | 10 | // Redefine types since all API groups import "github.com/ericchiang/k8s" 11 | // We can't use them here because it'll create a circular import cycle. 12 | 13 | type Pod struct { 14 | Metadata *metav1.ObjectMeta 15 | } 16 | 17 | type PodList struct { 18 | Metadata *metav1.ListMeta 19 | } 20 | 21 | func (p *Pod) GetMetadata() *metav1.ObjectMeta { return p.Metadata } 22 | func (p *PodList) GetMetadata() *metav1.ListMeta { return p.Metadata } 23 | 24 | type Deployment struct { 25 | Metadata *metav1.ObjectMeta 26 | } 27 | 28 | type DeploymentList struct { 29 | Metadata *metav1.ListMeta 30 | } 31 | 32 | func (p *Deployment) GetMetadata() *metav1.ObjectMeta { return p.Metadata } 33 | func (p *DeploymentList) GetMetadata() *metav1.ListMeta { return p.Metadata } 34 | 35 | type ClusterRole struct { 36 | Metadata *metav1.ObjectMeta 37 | } 38 | 39 | type ClusterRoleList struct { 40 | Metadata *metav1.ListMeta 41 | } 42 | 43 | func (p *ClusterRole) GetMetadata() *metav1.ObjectMeta { return p.Metadata } 44 | func (p *ClusterRoleList) GetMetadata() *metav1.ListMeta { return p.Metadata } 45 | 46 | func init() { 47 | Register("", "v1", "pods", true, &Pod{}) 48 | RegisterList("", "v1", "pods", true, &PodList{}) 49 | 50 | Register("apps", "v1beta2", "deployments", true, &Deployment{}) 51 | RegisterList("apps", "v1beta2", "deployments", true, &DeploymentList{}) 52 | 53 | Register("rbac.authorization.k8s.io", "v1", "clusterroles", false, &ClusterRole{}) 54 | RegisterList("rbac.authorization.k8s.io", "v1", "clusterroles", false, &ClusterRoleList{}) 55 | } 56 | 57 | func TestResourceURL(t *testing.T) { 58 | tests := []struct { 59 | name string 60 | endpoint string 61 | resource Resource 62 | withName bool 63 | options []Option 64 | want string 65 | wantErr bool 66 | }{ 67 | { 68 | name: "pod", 69 | endpoint: "https://example.com", 70 | resource: &Pod{ 71 | Metadata: &metav1.ObjectMeta{ 72 | Namespace: String("my-namespace"), 73 | Name: String("my-pod"), 74 | }, 75 | }, 76 | want: "https://example.com/api/v1/namespaces/my-namespace/pods", 77 | }, 78 | { 79 | name: "deployment", 80 | endpoint: "https://example.com", 81 | resource: &Deployment{ 82 | Metadata: &metav1.ObjectMeta{ 83 | Namespace: String("my-namespace"), 84 | Name: String("my-deployment"), 85 | }, 86 | }, 87 | want: "https://example.com/apis/apps/v1beta2/namespaces/my-namespace/deployments", 88 | }, 89 | { 90 | name: "deployment-with-name", 91 | endpoint: "https://example.com", 92 | resource: &Deployment{ 93 | Metadata: &metav1.ObjectMeta{ 94 | Namespace: String("my-namespace"), 95 | Name: String("my-deployment"), 96 | }, 97 | }, 98 | withName: true, 99 | want: "https://example.com/apis/apps/v1beta2/namespaces/my-namespace/deployments/my-deployment", 100 | }, 101 | { 102 | name: "deployment-with-subresource", 103 | endpoint: "https://example.com", 104 | resource: &Deployment{ 105 | Metadata: &metav1.ObjectMeta{ 106 | Namespace: String("my-namespace"), 107 | Name: String("my-deployment"), 108 | }, 109 | }, 110 | withName: true, 111 | options: []Option{ 112 | Subresource("status"), 113 | }, 114 | want: "https://example.com/apis/apps/v1beta2/namespaces/my-namespace/deployments/my-deployment/status", 115 | }, 116 | { 117 | name: "pod-with-timeout", 118 | endpoint: "https://example.com", 119 | resource: &Pod{ 120 | Metadata: &metav1.ObjectMeta{ 121 | Namespace: String("my-namespace"), 122 | Name: String("my-pod"), 123 | }, 124 | }, 125 | options: []Option{ 126 | Timeout(time.Minute), 127 | }, 128 | want: "https://example.com/api/v1/namespaces/my-namespace/pods?timeoutSeconds=60", 129 | }, 130 | { 131 | name: "pod-with-resource-version", 132 | endpoint: "https://example.com", 133 | resource: &Pod{ 134 | Metadata: &metav1.ObjectMeta{ 135 | Namespace: String("my-namespace"), 136 | Name: String("my-pod"), 137 | }, 138 | }, 139 | options: []Option{ 140 | ResourceVersion("foo"), 141 | }, 142 | want: "https://example.com/api/v1/namespaces/my-namespace/pods?resourceVersion=foo", 143 | }, 144 | } 145 | 146 | for _, test := range tests { 147 | t.Run(test.name, func(t *testing.T) { 148 | got, err := resourceURL(test.endpoint, test.resource, test.withName, test.options...) 149 | if err != nil { 150 | if test.wantErr { 151 | return 152 | } 153 | t.Fatalf("constructing resource URL: %v", err) 154 | } 155 | if test.wantErr { 156 | t.Fatal("expected error") 157 | } 158 | if test.want != got { 159 | t.Errorf("wanted=%q, got=%q", test.want, got) 160 | } 161 | }) 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /runtime/schema/generated.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: k8s.io/apimachinery/pkg/runtime/schema/generated.proto 3 | 4 | /* 5 | Package schema is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | k8s.io/apimachinery/pkg/runtime/schema/generated.proto 9 | 10 | It has these top-level messages: 11 | */ 12 | package schema 13 | 14 | import proto "github.com/golang/protobuf/proto" 15 | import fmt "fmt" 16 | import math "math" 17 | 18 | // Reference imports to suppress errors if they are not otherwise used. 19 | var _ = proto.Marshal 20 | var _ = fmt.Errorf 21 | var _ = math.Inf 22 | 23 | // This is a compile-time assertion to ensure that this generated file 24 | // is compatible with the proto package it is being compiled against. 25 | // A compilation error at this line likely means your copy of the 26 | // proto package needs to be updated. 27 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 28 | 29 | func init() { 30 | proto.RegisterFile("k8s.io/apimachinery/pkg/runtime/schema/generated.proto", fileDescriptorGenerated) 31 | } 32 | 33 | var fileDescriptorGenerated = []byte{ 34 | // 120 bytes of a gzipped FileDescriptorProto 35 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0xcb, 0xb6, 0x28, 0xd6, 36 | 0xcb, 0xcc, 0xd7, 0x4f, 0x2c, 0xc8, 0xcc, 0x4d, 0x4c, 0xce, 0xc8, 0xcc, 0x4b, 0x2d, 0xaa, 0xd4, 37 | 0x2f, 0xc8, 0x4e, 0xd7, 0x2f, 0x2a, 0xcd, 0x2b, 0xc9, 0xcc, 0x4d, 0xd5, 0x2f, 0x4e, 0xce, 0x48, 38 | 0xcd, 0x4d, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0x4a, 0x2c, 0x49, 0x4d, 0xd1, 0x2b, 0x28, 0xca, 39 | 0x2f, 0xc9, 0x17, 0x52, 0x83, 0xe8, 0xd3, 0x43, 0xd6, 0xa7, 0x57, 0x90, 0x9d, 0xae, 0x07, 0xd5, 40 | 0xa7, 0x07, 0xd1, 0xe7, 0x24, 0x71, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 41 | 0xc9, 0x31, 0xce, 0x78, 0x2c, 0xc7, 0x10, 0xc5, 0x06, 0x91, 0x01, 0x04, 0x00, 0x00, 0xff, 0xff, 42 | 0x35, 0x7a, 0x0d, 0x14, 0x7a, 0x00, 0x00, 0x00, 43 | } 44 | -------------------------------------------------------------------------------- /scripts/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | TEMPDIR=$(mktemp -d) 4 | mkdir -p $TEMPDIR/src/github.com/golang 5 | ln -s $PWD/_output/src/github.com/golang/protobuf $TEMPDIR/src/github.com/golang/protobuf 6 | function cleanup { 7 | unlink $TEMPDIR/src/github.com/golang/protobuf 8 | rm -rf $TEMPDIR 9 | } 10 | trap cleanup EXIT 11 | 12 | # Ensure we're using protoc and gomvpkg that this repo downloads. 13 | export PATH=$PWD/_output/bin:$PATH 14 | 15 | # Copy all .proto files from Kubernetes into a temporary directory. 16 | REPOS=( "apimachinery" "api" "apiextensions-apiserver" "kube-aggregator" ) 17 | for REPO in "${REPOS[@]}"; do 18 | SOURCE=$PWD/_output/kubernetes/staging/src/k8s.io/$REPO 19 | TARGET=$TEMPDIR/src/k8s.io 20 | mkdir -p $TARGET 21 | rsync -a --prune-empty-dirs --include '*/' --include '*.proto' --exclude '*' $SOURCE $TARGET 22 | done 23 | 24 | # Remove API groups that aren't actually real 25 | rm -r $TEMPDIR/src/k8s.io/apimachinery/pkg/apis/testapigroup 26 | 27 | cd $TEMPDIR/src 28 | for FILE in $( find . -type f ); do 29 | protoc --gofast_out=. $FILE 30 | done 31 | rm $( find . -type f -name '*.proto' ); 32 | cd - 33 | 34 | export GOPATH=$TEMPDIR 35 | function mvpkg { 36 | FROM="k8s.io/$1" 37 | TO="github.com/ericchiang/k8s/$2" 38 | mkdir -p "$GOPATH/src/$(dirname $TO)" 39 | echo "gompvpkg -from=$FROM -to=$TO" 40 | gomvpkg -from=$FROM -to=$TO 41 | } 42 | 43 | mvpkg apiextensions-apiserver/pkg/apis/apiextensions/v1beta1 apis/apiextensions/v1beta1 44 | mvpkg apimachinery/pkg/api/resource apis/resource 45 | mvpkg apimachinery/pkg/apis/meta apis/meta 46 | mvpkg apimachinery/pkg/runtime runtime 47 | mvpkg apimachinery/pkg/util util 48 | for DIR in $( ls ${TEMPDIR}/src/k8s.io/api/ ); do 49 | mvpkg api/$DIR apis/$DIR 50 | done 51 | mvpkg kube-aggregator/pkg/apis/apiregistration apis/apiregistration 52 | 53 | rm -rf api apis runtime util 54 | mv $TEMPDIR/src/github.com/ericchiang/k8s/* . 55 | -------------------------------------------------------------------------------- /scripts/get-protoc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | OS=$(uname) 4 | if [ "$OS" == "Darwin" ]; then 5 | OS="osx" 6 | fi 7 | 8 | curl -L -o _output/protoc.zip https://github.com/google/protobuf/releases/download/v3.5.1/protoc-3.5.1-${OS}-x86_64.zip 9 | bsdtar -x -f _output/protoc.zip -C _output bin/protoc 10 | -------------------------------------------------------------------------------- /scripts/git-diff.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | DIFF=$( git diff . ) 4 | if [ "$DIFF" != "" ]; then 5 | echo "$DIFF" >&2 6 | exit 1 7 | fi 8 | -------------------------------------------------------------------------------- /scripts/go-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | function usage { 4 | >&2 echo "./go-install.sh [repo] [repo import path] [tool import path] [rev]" 5 | } 6 | 7 | REPO=$1 8 | REPO_ROOT=$2 9 | TOOL=$3 10 | REV=$4 11 | 12 | git clone $REPO _output/src/$REPO_ROOT 13 | cd _output/src/$REPO_ROOT 14 | git checkout $REV 15 | cd - 16 | GOPATH=$PWD/_output GOBIN=$PWD/_output/bin go install -v $TOOL 17 | -------------------------------------------------------------------------------- /scripts/json.go.partial: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | ) 7 | 8 | // JSON marshaling logic for the Time type so it can be used for custom 9 | // resources, which serialize to JSON. 10 | 11 | func (t Time) MarshalJSON() ([]byte, error) { 12 | var seconds, nanos int64 13 | if t.Seconds != nil { 14 | seconds = *t.Seconds 15 | } 16 | if t.Nanos != nil { 17 | nanos = int64(*t.Nanos) 18 | } 19 | return json.Marshal(time.Unix(seconds, nanos)) 20 | } 21 | 22 | func (t *Time) UnmarshalJSON(p []byte) error { 23 | var t1 time.Time 24 | if err := json.Unmarshal(p, &t1); err != nil { 25 | return err 26 | } 27 | seconds := t1.Unix() 28 | nanos := int32(t1.UnixNano()) 29 | t.Seconds = &seconds 30 | t.Nanos = &nanos 31 | return nil 32 | } 33 | 34 | // Status must implement json.Unmarshaler for the codec to deserialize a JSON 35 | // payload into it. 36 | // 37 | // See https://github.com/ericchiang/k8s/issues/82 38 | 39 | type jsonStatus Status 40 | 41 | func (s *Status) UnmarshalJSON(data []byte) error { 42 | var j jsonStatus 43 | if err := json.Unmarshal(data, &j); err != nil { 44 | return err 45 | } 46 | *s = Status(j) 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /scripts/kubeconfig: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Config 3 | clusters: 4 | - name: local 5 | cluster: 6 | server: http://localhost:8080 7 | users: 8 | - name: local 9 | user: 10 | contexts: 11 | - context: 12 | cluster: local 13 | user: local 14 | -------------------------------------------------------------------------------- /scripts/register.go: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "bytes" 7 | "go/format" 8 | "html/template" 9 | "io/ioutil" 10 | "log" 11 | "path/filepath" 12 | "strings" 13 | ) 14 | 15 | type Resource struct { 16 | GoType string 17 | // Plural name of the resource. If empty, the GoType lowercased + "s". 18 | Name string 19 | Flags uint8 20 | } 21 | 22 | const ( 23 | // Is the resource cluster scoped (e.g. "nodes")? 24 | NotNamespaced uint8 = 1 << iota 25 | // Many "review" resources can be created but not listed 26 | NoList uint8 = 1 << iota 27 | ) 28 | 29 | type APIGroup struct { 30 | Package string 31 | Group string 32 | Versions map[string][]Resource 33 | } 34 | 35 | func init() { 36 | for _, group := range apiGroups { 37 | for _, resources := range group.Versions { 38 | for i, r := range resources { 39 | if r.Name == "" { 40 | r.Name = strings.ToLower(r.GoType) + "s" 41 | } 42 | resources[i] = r 43 | } 44 | } 45 | } 46 | } 47 | 48 | var apiGroups = []APIGroup{ 49 | { 50 | Package: "admissionregistration", 51 | Group: "admissionregistration.k8s.io", 52 | Versions: map[string][]Resource{ 53 | "v1beta1": []Resource{ 54 | {"MutatingWebhookConfiguration", "", NotNamespaced}, 55 | {"ValidatingWebhookConfiguration", "", NotNamespaced}, 56 | }, 57 | "v1alpha1": []Resource{ 58 | {"InitializerConfiguration", "", NotNamespaced}, 59 | }, 60 | }, 61 | }, 62 | { 63 | Package: "apiextensions", 64 | Group: "apiextensions.k8s.io", 65 | Versions: map[string][]Resource{ 66 | "v1beta1": []Resource{ 67 | {"CustomResourceDefinition", "", NotNamespaced}, 68 | }, 69 | }, 70 | }, 71 | { 72 | Package: "apiregistration", 73 | Group: "apiregistration.k8s.io", 74 | Versions: map[string][]Resource{ 75 | "v1": []Resource{ 76 | {"APIService", "", NotNamespaced}, 77 | }, 78 | "v1beta1": []Resource{ 79 | {"APIService", "", NotNamespaced}, 80 | }, 81 | }, 82 | }, 83 | { 84 | Package: "apps", 85 | Group: "apps", 86 | Versions: map[string][]Resource{ 87 | "v1": []Resource{ 88 | {"ControllerRevision", "", 0}, 89 | {"DaemonSet", "", 0}, 90 | {"Deployment", "", 0}, 91 | {"ReplicaSet", "", 0}, 92 | {"StatefulSet", "", 0}, 93 | }, 94 | "v1beta2": []Resource{ 95 | {"ControllerRevision", "", 0}, 96 | {"DaemonSet", "", 0}, 97 | {"Deployment", "", 0}, 98 | {"ReplicaSet", "", 0}, 99 | {"StatefulSet", "", 0}, 100 | }, 101 | "v1beta1": []Resource{ 102 | {"ControllerRevision", "", 0}, 103 | {"Deployment", "", 0}, 104 | {"StatefulSet", "", 0}, 105 | }, 106 | }, 107 | }, 108 | { 109 | Package: "authentication", 110 | Group: "authentication.k8s.io", 111 | Versions: map[string][]Resource{ 112 | "v1": []Resource{ 113 | {"TokenReview", "", NotNamespaced | NoList}, 114 | {"TokenRequest", "", NotNamespaced | NoList}, 115 | }, 116 | "v1beta1": []Resource{ 117 | {"TokenReview", "", NotNamespaced | NoList}, 118 | }, 119 | }, 120 | }, 121 | { 122 | Package: "authorization", 123 | Group: "authorization.k8s.io", 124 | Versions: map[string][]Resource{ 125 | "v1": []Resource{ 126 | {"LocalSubjectAccessReview", "", NoList}, 127 | {"SelfSubjectAccessReview", "", NotNamespaced | NoList}, 128 | {"SelfSubjectRulesReview", "", NotNamespaced | NoList}, 129 | {"SubjectAccessReview", "", NotNamespaced | NoList}, 130 | }, 131 | "v1beta1": []Resource{ 132 | {"LocalSubjectAccessReview", "", NoList}, 133 | {"SelfSubjectAccessReview", "", NotNamespaced | NoList}, 134 | {"SelfSubjectRulesReview", "", NotNamespaced | NoList}, 135 | {"SubjectAccessReview", "", NotNamespaced | NoList}, 136 | }, 137 | }, 138 | }, 139 | { 140 | Package: "autoscaling", 141 | Group: "autoscaling", 142 | Versions: map[string][]Resource{ 143 | "v1": []Resource{ 144 | {"HorizontalPodAutoscaler", "", 0}, 145 | }, 146 | "v2beta1": []Resource{ 147 | {"HorizontalPodAutoscaler", "", 0}, 148 | }, 149 | }, 150 | }, 151 | { 152 | Package: "batch", 153 | Group: "batch", 154 | Versions: map[string][]Resource{ 155 | "v1": []Resource{ 156 | {"Job", "", 0}, 157 | }, 158 | "v1beta1": []Resource{ 159 | {"CronJob", "", 0}, 160 | }, 161 | "v2alpha1": []Resource{ 162 | {"CronJob", "", 0}, 163 | }, 164 | }, 165 | }, 166 | { 167 | Package: "certificates", 168 | Group: "certificates.k8s.io", 169 | Versions: map[string][]Resource{ 170 | "v1beta1": []Resource{ 171 | {"CertificateSigningRequest", "", NotNamespaced}, 172 | }, 173 | }, 174 | }, 175 | { 176 | Package: "core", 177 | Group: "", 178 | Versions: map[string][]Resource{ 179 | "v1": []Resource{ 180 | {"ComponentStatus", "componentstatuses", NotNamespaced}, 181 | {"ConfigMap", "", 0}, 182 | {"Endpoints", "endpoints", 0}, 183 | {"LimitRange", "", 0}, 184 | {"Namespace", "", NotNamespaced}, 185 | {"Node", "", NotNamespaced}, 186 | {"PersistentVolumeClaim", "", 0}, 187 | {"PersistentVolume", "", NotNamespaced}, 188 | {"Pod", "", 0}, 189 | {"ReplicationController", "", 0}, 190 | {"ResourceQuota", "", 0}, 191 | {"Secret", "", 0}, 192 | {"Service", "", 0}, 193 | {"ServiceAccount", "", 0}, 194 | {"Event", "", 0}, 195 | }, 196 | }, 197 | }, 198 | { 199 | Package: "events", 200 | Group: "events.k8s.io", 201 | Versions: map[string][]Resource{ 202 | "v1beta1": []Resource{ 203 | {"Event", "", 0}, 204 | }, 205 | }, 206 | }, 207 | { 208 | Package: "extensions", 209 | Group: "extensions", 210 | Versions: map[string][]Resource{ 211 | "v1beta1": []Resource{ 212 | {"DaemonSet", "", 0}, 213 | {"Deployment", "", 0}, 214 | {"Ingress", "ingresses", 0}, 215 | {"NetworkPolicy", "networkpolicies", 0}, 216 | {"PodSecurityPolicy", "podsecuritypolicies", NotNamespaced}, 217 | {"ReplicaSet", "", 0}, 218 | }, 219 | }, 220 | }, 221 | { 222 | Package: "networking", 223 | Group: "networking.k8s.io", 224 | Versions: map[string][]Resource{ 225 | "v1": []Resource{ 226 | {"NetworkPolicy", "networkpolicies", 0}, 227 | }, 228 | }, 229 | }, 230 | { 231 | Package: "policy", 232 | Group: "policy", 233 | Versions: map[string][]Resource{ 234 | "v1beta1": []Resource{ 235 | {"PodDisruptionBudget", "", 0}, 236 | {"PodSecurityPolicy", "podsecuritypolicies", NotNamespaced}, 237 | }, 238 | }, 239 | }, 240 | { 241 | Package: "rbac", 242 | Group: "rbac.authorization.k8s.io", 243 | Versions: map[string][]Resource{ 244 | "v1": []Resource{ 245 | {"ClusterRole", "", NotNamespaced}, 246 | {"ClusterRoleBinding", "", NotNamespaced}, 247 | {"Role", "", 0}, 248 | {"RoleBinding", "", 0}, 249 | }, 250 | "v1beta1": []Resource{ 251 | {"ClusterRole", "", NotNamespaced}, 252 | {"ClusterRoleBinding", "", NotNamespaced}, 253 | {"Role", "", 0}, 254 | {"RoleBinding", "", 0}, 255 | }, 256 | "v1alpha1": []Resource{ 257 | {"ClusterRole", "", NotNamespaced}, 258 | {"ClusterRoleBinding", "", NotNamespaced}, 259 | {"Role", "", 0}, 260 | {"RoleBinding", "", 0}, 261 | }, 262 | }, 263 | }, 264 | { 265 | Package: "scheduling", 266 | Group: "scheduling.k8s.io", 267 | Versions: map[string][]Resource{ 268 | "v1alpha1": []Resource{ 269 | {"PriorityClass", "priorityclasses", NotNamespaced}, 270 | }, 271 | }, 272 | }, 273 | { 274 | Package: "settings", 275 | Group: "settings.k8s.io", 276 | Versions: map[string][]Resource{ 277 | "v1alpha1": []Resource{ 278 | {"PodPreset", "", 0}, 279 | }, 280 | }, 281 | }, 282 | { 283 | Package: "storage", 284 | Group: "storage.k8s.io", 285 | Versions: map[string][]Resource{ 286 | "v1": []Resource{ 287 | {"StorageClass", "storageclasses", NotNamespaced}, 288 | }, 289 | "v1beta1": []Resource{ 290 | {"StorageClass", "storageclasses", NotNamespaced}, 291 | }, 292 | "v1alpha1": []Resource{ 293 | {"VolumeAttachment", "", NotNamespaced}, 294 | }, 295 | }, 296 | }, 297 | } 298 | 299 | type templateData struct { 300 | Package string 301 | Resources []templateResource 302 | } 303 | 304 | type templateResource struct { 305 | Group string 306 | Version string 307 | Name string 308 | Type string 309 | Namespaced bool 310 | List bool 311 | } 312 | 313 | var tmpl = template.Must(template.New("").Parse(`package {{ .Package }} 314 | 315 | import "github.com/ericchiang/k8s" 316 | 317 | func init() { 318 | {{- range $i, $r := .Resources -}} 319 | k8s.Register("{{ $r.Group }}", "{{ $r.Version }}", "{{ $r.Name }}", {{ $r.Namespaced }}, &{{ $r.Type }}{}) 320 | {{ end -}} 321 | {{- range $i, $r := .Resources -}}{{ if $r.List }} 322 | k8s.RegisterList("{{ $r.Group }}", "{{ $r.Version }}", "{{ $r.Name }}", {{ $r.Namespaced }}, &{{ $r.Type }}List{}){{ end -}} 323 | {{ end -}} 324 | } 325 | `)) 326 | 327 | func main() { 328 | for _, group := range apiGroups { 329 | for version, resources := range group.Versions { 330 | fp := filepath.Join("apis", group.Package, version, "register.go") 331 | data := templateData{Package: version} 332 | for _, r := range resources { 333 | data.Resources = append(data.Resources, templateResource{ 334 | Group: group.Group, 335 | Version: version, 336 | Name: r.Name, 337 | Type: r.GoType, 338 | Namespaced: r.Flags&NotNamespaced == 0, 339 | List: r.Flags&NoList == 0, 340 | }) 341 | } 342 | 343 | buff := new(bytes.Buffer) 344 | if err := tmpl.Execute(buff, &data); err != nil { 345 | log.Fatal(err) 346 | } 347 | out, err := format.Source(buff.Bytes()) 348 | if err != nil { 349 | log.Fatal(err) 350 | } 351 | if err := ioutil.WriteFile(fp, out, 0644); err != nil { 352 | log.Fatal(err) 353 | } 354 | } 355 | } 356 | } 357 | -------------------------------------------------------------------------------- /scripts/run-kube.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | TEMPDIR=$( mktemp -d ) 4 | 5 | docker run \ 6 | --cidfile=$TEMPDIR/etcd \ 7 | -d \ 8 | --net=host \ 9 | gcr.io/google_containers/etcd:3.1.10 \ 10 | etcd 11 | 12 | docker run \ 13 | --cidfile=$TEMPDIR/kube-apiserver \ 14 | -d \ 15 | -v $TEMPDIR:/var/run/kube-test:ro \ 16 | --net=host \ 17 | gcr.io/google_containers/kube-apiserver-amd64:v1.10.4 \ 18 | kube-apiserver \ 19 | --etcd-servers=http://localhost:2379 \ 20 | --service-cluster-ip-range=10.0.0.1/16 \ 21 | --insecure-bind-address=0.0.0.0 \ 22 | --insecure-port=8080 23 | 24 | until $(curl --output /dev/null --silent --head --fail http://localhost:8080/healthz); do 25 | printf '.' 26 | sleep 1 27 | done 28 | echo "API server ready" 29 | -------------------------------------------------------------------------------- /util/intstr/generated.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: k8s.io/apimachinery/pkg/util/intstr/generated.proto 3 | 4 | /* 5 | Package intstr is a generated protocol buffer package. 6 | 7 | It is generated from these files: 8 | k8s.io/apimachinery/pkg/util/intstr/generated.proto 9 | 10 | It has these top-level messages: 11 | IntOrString 12 | */ 13 | package intstr 14 | 15 | import proto "github.com/golang/protobuf/proto" 16 | import fmt "fmt" 17 | import math "math" 18 | 19 | import io "io" 20 | 21 | // Reference imports to suppress errors if they are not otherwise used. 22 | var _ = proto.Marshal 23 | var _ = fmt.Errorf 24 | var _ = math.Inf 25 | 26 | // This is a compile-time assertion to ensure that this generated file 27 | // is compatible with the proto package it is being compiled against. 28 | // A compilation error at this line likely means your copy of the 29 | // proto package needs to be updated. 30 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 31 | 32 | // IntOrString is a type that can hold an int32 or a string. When used in 33 | // JSON or YAML marshalling and unmarshalling, it produces or consumes the 34 | // inner type. This allows you to have, for example, a JSON field that can 35 | // accept a name or number. 36 | // TODO: Rename to Int32OrString 37 | // 38 | // +protobuf=true 39 | // +protobuf.options.(gogoproto.goproto_stringer)=false 40 | // +k8s:openapi-gen=true 41 | type IntOrString struct { 42 | Type *int64 `protobuf:"varint,1,opt,name=type" json:"type,omitempty"` 43 | IntVal *int32 `protobuf:"varint,2,opt,name=intVal" json:"intVal,omitempty"` 44 | StrVal *string `protobuf:"bytes,3,opt,name=strVal" json:"strVal,omitempty"` 45 | XXX_unrecognized []byte `json:"-"` 46 | } 47 | 48 | func (m *IntOrString) Reset() { *m = IntOrString{} } 49 | func (m *IntOrString) String() string { return proto.CompactTextString(m) } 50 | func (*IntOrString) ProtoMessage() {} 51 | func (*IntOrString) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} } 52 | 53 | func (m *IntOrString) GetType() int64 { 54 | if m != nil && m.Type != nil { 55 | return *m.Type 56 | } 57 | return 0 58 | } 59 | 60 | func (m *IntOrString) GetIntVal() int32 { 61 | if m != nil && m.IntVal != nil { 62 | return *m.IntVal 63 | } 64 | return 0 65 | } 66 | 67 | func (m *IntOrString) GetStrVal() string { 68 | if m != nil && m.StrVal != nil { 69 | return *m.StrVal 70 | } 71 | return "" 72 | } 73 | 74 | func init() { 75 | proto.RegisterType((*IntOrString)(nil), "k8s.io.apimachinery.pkg.util.intstr.IntOrString") 76 | } 77 | func (m *IntOrString) Marshal() (dAtA []byte, err error) { 78 | size := m.Size() 79 | dAtA = make([]byte, size) 80 | n, err := m.MarshalTo(dAtA) 81 | if err != nil { 82 | return nil, err 83 | } 84 | return dAtA[:n], nil 85 | } 86 | 87 | func (m *IntOrString) MarshalTo(dAtA []byte) (int, error) { 88 | var i int 89 | _ = i 90 | var l int 91 | _ = l 92 | if m.Type != nil { 93 | dAtA[i] = 0x8 94 | i++ 95 | i = encodeVarintGenerated(dAtA, i, uint64(*m.Type)) 96 | } 97 | if m.IntVal != nil { 98 | dAtA[i] = 0x10 99 | i++ 100 | i = encodeVarintGenerated(dAtA, i, uint64(*m.IntVal)) 101 | } 102 | if m.StrVal != nil { 103 | dAtA[i] = 0x1a 104 | i++ 105 | i = encodeVarintGenerated(dAtA, i, uint64(len(*m.StrVal))) 106 | i += copy(dAtA[i:], *m.StrVal) 107 | } 108 | if m.XXX_unrecognized != nil { 109 | i += copy(dAtA[i:], m.XXX_unrecognized) 110 | } 111 | return i, nil 112 | } 113 | 114 | func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int { 115 | for v >= 1<<7 { 116 | dAtA[offset] = uint8(v&0x7f | 0x80) 117 | v >>= 7 118 | offset++ 119 | } 120 | dAtA[offset] = uint8(v) 121 | return offset + 1 122 | } 123 | func (m *IntOrString) Size() (n int) { 124 | var l int 125 | _ = l 126 | if m.Type != nil { 127 | n += 1 + sovGenerated(uint64(*m.Type)) 128 | } 129 | if m.IntVal != nil { 130 | n += 1 + sovGenerated(uint64(*m.IntVal)) 131 | } 132 | if m.StrVal != nil { 133 | l = len(*m.StrVal) 134 | n += 1 + l + sovGenerated(uint64(l)) 135 | } 136 | if m.XXX_unrecognized != nil { 137 | n += len(m.XXX_unrecognized) 138 | } 139 | return n 140 | } 141 | 142 | func sovGenerated(x uint64) (n int) { 143 | for { 144 | n++ 145 | x >>= 7 146 | if x == 0 { 147 | break 148 | } 149 | } 150 | return n 151 | } 152 | func sozGenerated(x uint64) (n int) { 153 | return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63)))) 154 | } 155 | func (m *IntOrString) Unmarshal(dAtA []byte) error { 156 | l := len(dAtA) 157 | iNdEx := 0 158 | for iNdEx < l { 159 | preIndex := iNdEx 160 | var wire uint64 161 | for shift := uint(0); ; shift += 7 { 162 | if shift >= 64 { 163 | return ErrIntOverflowGenerated 164 | } 165 | if iNdEx >= l { 166 | return io.ErrUnexpectedEOF 167 | } 168 | b := dAtA[iNdEx] 169 | iNdEx++ 170 | wire |= (uint64(b) & 0x7F) << shift 171 | if b < 0x80 { 172 | break 173 | } 174 | } 175 | fieldNum := int32(wire >> 3) 176 | wireType := int(wire & 0x7) 177 | if wireType == 4 { 178 | return fmt.Errorf("proto: IntOrString: wiretype end group for non-group") 179 | } 180 | if fieldNum <= 0 { 181 | return fmt.Errorf("proto: IntOrString: illegal tag %d (wire type %d)", fieldNum, wire) 182 | } 183 | switch fieldNum { 184 | case 1: 185 | if wireType != 0 { 186 | return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) 187 | } 188 | var v int64 189 | for shift := uint(0); ; shift += 7 { 190 | if shift >= 64 { 191 | return ErrIntOverflowGenerated 192 | } 193 | if iNdEx >= l { 194 | return io.ErrUnexpectedEOF 195 | } 196 | b := dAtA[iNdEx] 197 | iNdEx++ 198 | v |= (int64(b) & 0x7F) << shift 199 | if b < 0x80 { 200 | break 201 | } 202 | } 203 | m.Type = &v 204 | case 2: 205 | if wireType != 0 { 206 | return fmt.Errorf("proto: wrong wireType = %d for field IntVal", wireType) 207 | } 208 | var v int32 209 | for shift := uint(0); ; shift += 7 { 210 | if shift >= 64 { 211 | return ErrIntOverflowGenerated 212 | } 213 | if iNdEx >= l { 214 | return io.ErrUnexpectedEOF 215 | } 216 | b := dAtA[iNdEx] 217 | iNdEx++ 218 | v |= (int32(b) & 0x7F) << shift 219 | if b < 0x80 { 220 | break 221 | } 222 | } 223 | m.IntVal = &v 224 | case 3: 225 | if wireType != 2 { 226 | return fmt.Errorf("proto: wrong wireType = %d for field StrVal", wireType) 227 | } 228 | var stringLen uint64 229 | for shift := uint(0); ; shift += 7 { 230 | if shift >= 64 { 231 | return ErrIntOverflowGenerated 232 | } 233 | if iNdEx >= l { 234 | return io.ErrUnexpectedEOF 235 | } 236 | b := dAtA[iNdEx] 237 | iNdEx++ 238 | stringLen |= (uint64(b) & 0x7F) << shift 239 | if b < 0x80 { 240 | break 241 | } 242 | } 243 | intStringLen := int(stringLen) 244 | if intStringLen < 0 { 245 | return ErrInvalidLengthGenerated 246 | } 247 | postIndex := iNdEx + intStringLen 248 | if postIndex > l { 249 | return io.ErrUnexpectedEOF 250 | } 251 | s := string(dAtA[iNdEx:postIndex]) 252 | m.StrVal = &s 253 | iNdEx = postIndex 254 | default: 255 | iNdEx = preIndex 256 | skippy, err := skipGenerated(dAtA[iNdEx:]) 257 | if err != nil { 258 | return err 259 | } 260 | if skippy < 0 { 261 | return ErrInvalidLengthGenerated 262 | } 263 | if (iNdEx + skippy) > l { 264 | return io.ErrUnexpectedEOF 265 | } 266 | m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) 267 | iNdEx += skippy 268 | } 269 | } 270 | 271 | if iNdEx > l { 272 | return io.ErrUnexpectedEOF 273 | } 274 | return nil 275 | } 276 | func skipGenerated(dAtA []byte) (n int, err error) { 277 | l := len(dAtA) 278 | iNdEx := 0 279 | for iNdEx < l { 280 | var wire uint64 281 | for shift := uint(0); ; shift += 7 { 282 | if shift >= 64 { 283 | return 0, ErrIntOverflowGenerated 284 | } 285 | if iNdEx >= l { 286 | return 0, io.ErrUnexpectedEOF 287 | } 288 | b := dAtA[iNdEx] 289 | iNdEx++ 290 | wire |= (uint64(b) & 0x7F) << shift 291 | if b < 0x80 { 292 | break 293 | } 294 | } 295 | wireType := int(wire & 0x7) 296 | switch wireType { 297 | case 0: 298 | for shift := uint(0); ; shift += 7 { 299 | if shift >= 64 { 300 | return 0, ErrIntOverflowGenerated 301 | } 302 | if iNdEx >= l { 303 | return 0, io.ErrUnexpectedEOF 304 | } 305 | iNdEx++ 306 | if dAtA[iNdEx-1] < 0x80 { 307 | break 308 | } 309 | } 310 | return iNdEx, nil 311 | case 1: 312 | iNdEx += 8 313 | return iNdEx, nil 314 | case 2: 315 | var length int 316 | for shift := uint(0); ; shift += 7 { 317 | if shift >= 64 { 318 | return 0, ErrIntOverflowGenerated 319 | } 320 | if iNdEx >= l { 321 | return 0, io.ErrUnexpectedEOF 322 | } 323 | b := dAtA[iNdEx] 324 | iNdEx++ 325 | length |= (int(b) & 0x7F) << shift 326 | if b < 0x80 { 327 | break 328 | } 329 | } 330 | iNdEx += length 331 | if length < 0 { 332 | return 0, ErrInvalidLengthGenerated 333 | } 334 | return iNdEx, nil 335 | case 3: 336 | for { 337 | var innerWire uint64 338 | var start int = iNdEx 339 | for shift := uint(0); ; shift += 7 { 340 | if shift >= 64 { 341 | return 0, ErrIntOverflowGenerated 342 | } 343 | if iNdEx >= l { 344 | return 0, io.ErrUnexpectedEOF 345 | } 346 | b := dAtA[iNdEx] 347 | iNdEx++ 348 | innerWire |= (uint64(b) & 0x7F) << shift 349 | if b < 0x80 { 350 | break 351 | } 352 | } 353 | innerWireType := int(innerWire & 0x7) 354 | if innerWireType == 4 { 355 | break 356 | } 357 | next, err := skipGenerated(dAtA[start:]) 358 | if err != nil { 359 | return 0, err 360 | } 361 | iNdEx = start + next 362 | } 363 | return iNdEx, nil 364 | case 4: 365 | return iNdEx, nil 366 | case 5: 367 | iNdEx += 4 368 | return iNdEx, nil 369 | default: 370 | return 0, fmt.Errorf("proto: illegal wireType %d", wireType) 371 | } 372 | } 373 | panic("unreachable") 374 | } 375 | 376 | var ( 377 | ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling") 378 | ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow") 379 | ) 380 | 381 | func init() { 382 | proto.RegisterFile("k8s.io/apimachinery/pkg/util/intstr/generated.proto", fileDescriptorGenerated) 383 | } 384 | 385 | var fileDescriptorGenerated = []byte{ 386 | // 182 bytes of a gzipped FileDescriptorProto 387 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0xce, 0xb6, 0x28, 0xd6, 388 | 0xcb, 0xcc, 0xd7, 0x4f, 0x2c, 0xc8, 0xcc, 0x4d, 0x4c, 0xce, 0xc8, 0xcc, 0x4b, 0x2d, 0xaa, 0xd4, 389 | 0x2f, 0xc8, 0x4e, 0xd7, 0x2f, 0x2d, 0xc9, 0xcc, 0xd1, 0xcf, 0xcc, 0x2b, 0x29, 0x2e, 0x29, 0xd2, 390 | 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0x4a, 0x2c, 0x49, 0x4d, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 391 | 0x52, 0x86, 0x68, 0xd2, 0x43, 0xd6, 0xa4, 0x57, 0x90, 0x9d, 0xae, 0x07, 0xd2, 0xa4, 0x07, 0xd1, 392 | 0xa4, 0x14, 0xc8, 0xc5, 0xed, 0x99, 0x57, 0xe2, 0x5f, 0x14, 0x5c, 0x52, 0x94, 0x99, 0x97, 0x2e, 393 | 0x24, 0xc4, 0xc5, 0x52, 0x52, 0x59, 0x90, 0x2a, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x1c, 0x04, 0x66, 394 | 0x0b, 0x89, 0x71, 0xb1, 0x65, 0xe6, 0x95, 0x84, 0x25, 0xe6, 0x48, 0x30, 0x29, 0x30, 0x6a, 0xb0, 395 | 0x06, 0x41, 0x79, 0x20, 0xf1, 0xe2, 0x92, 0x22, 0x90, 0x38, 0xb3, 0x02, 0xa3, 0x06, 0x67, 0x10, 396 | 0x94, 0xe7, 0x24, 0x71, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 397 | 0xce, 0x78, 0x2c, 0xc7, 0x10, 0xc5, 0x06, 0xb1, 0x0c, 0x10, 0x00, 0x00, 0xff, 0xff, 0x2f, 0xa6, 398 | 0x56, 0x6e, 0xc7, 0x00, 0x00, 0x00, 399 | } 400 | -------------------------------------------------------------------------------- /watch.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/binary" 7 | "encoding/json" 8 | "errors" 9 | "fmt" 10 | "io" 11 | "io/ioutil" 12 | 13 | metav1 "github.com/ericchiang/k8s/apis/meta/v1" 14 | "github.com/ericchiang/k8s/runtime" 15 | "github.com/golang/protobuf/proto" 16 | ) 17 | 18 | // Decode events from a watch stream. 19 | // 20 | // See: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/protobuf.md#streaming-wire-format 21 | 22 | // Watcher receives a stream of events tracking a particular resource within 23 | // a namespace or across all namespaces. 24 | // 25 | // Watcher does not automatically reconnect. If a watch fails, a new watch must 26 | // be initialized. 27 | type Watcher struct { 28 | watcher interface { 29 | Next(Resource) (string, error) 30 | Close() error 31 | } 32 | } 33 | 34 | // Next decodes the next event from the watch stream. Errors are fatal, and 35 | // indicate that the watcher should no longer be used, and must be recreated. 36 | func (w *Watcher) Next(r Resource) (string, error) { 37 | return w.watcher.Next(r) 38 | } 39 | 40 | // Close closes the active connection with the API server being used for 41 | // the watch. 42 | func (w *Watcher) Close() error { 43 | return w.watcher.Close() 44 | } 45 | 46 | type watcherJSON struct { 47 | d *json.Decoder 48 | c io.Closer 49 | } 50 | 51 | func (w *watcherJSON) Close() error { 52 | return w.c.Close() 53 | } 54 | 55 | func (w *watcherJSON) Next(r Resource) (string, error) { 56 | var event struct { 57 | Type string `json:"type"` 58 | Object json.RawMessage `json:"object"` 59 | } 60 | if err := w.d.Decode(&event); err != nil { 61 | return "", fmt.Errorf("decode event: %v", err) 62 | } 63 | if event.Type == "" { 64 | return "", errors.New("watch event had no type field") 65 | } 66 | if event.Type == EventError { 67 | status := &metav1.Status{} 68 | if err := json.Unmarshal([]byte(event.Object), status); err != nil { 69 | return "", fmt.Errorf("decoding event error: %v", err) 70 | } 71 | return event.Type, &APIError{ 72 | Status: status, 73 | Code: int(*status.Code), 74 | } 75 | } 76 | if err := json.Unmarshal([]byte(event.Object), r); err != nil { 77 | return "", fmt.Errorf("decode resource: %v", err) 78 | } 79 | return event.Type, nil 80 | } 81 | 82 | type watcherPB struct { 83 | r io.ReadCloser 84 | } 85 | 86 | func (w *watcherPB) Next(r Resource) (string, error) { 87 | msg, ok := r.(proto.Message) 88 | if !ok { 89 | return "", errors.New("object was not a protobuf message") 90 | } 91 | event, unknown, err := w.next() 92 | if err != nil { 93 | return "", err 94 | } 95 | if event.Type == nil || *event.Type == "" { 96 | return "", errors.New("watch event had no type field") 97 | } 98 | if *event.Type == EventError { 99 | status := &metav1.Status{} 100 | if err := proto.Unmarshal(unknown.Raw, status); err != nil { 101 | return "", fmt.Errorf("decoding event error: %v", err) 102 | } 103 | return *event.Type, &APIError{ 104 | Status: status, 105 | Code: int(*status.Code), 106 | } 107 | } 108 | if err := proto.Unmarshal(unknown.Raw, msg); err != nil { 109 | return "", err 110 | } 111 | return *event.Type, nil 112 | } 113 | 114 | func (w *watcherPB) Close() error { 115 | return w.r.Close() 116 | } 117 | 118 | func (w *watcherPB) next() (*metav1.WatchEvent, *runtime.Unknown, error) { 119 | length := make([]byte, 4) 120 | if _, err := io.ReadFull(w.r, length); err != nil { 121 | return nil, nil, err 122 | } 123 | 124 | body := make([]byte, int(binary.BigEndian.Uint32(length))) 125 | if _, err := io.ReadFull(w.r, body); err != nil { 126 | return nil, nil, fmt.Errorf("read frame body: %v", err) 127 | } 128 | 129 | var event metav1.WatchEvent 130 | if err := proto.Unmarshal(body, &event); err != nil { 131 | return nil, nil, err 132 | } 133 | 134 | if event.Object == nil { 135 | return nil, nil, fmt.Errorf("event had no underlying object") 136 | } 137 | 138 | unknown, err := parseUnknown(event.Object.Raw) 139 | if err != nil { 140 | return nil, nil, err 141 | } 142 | 143 | return &event, unknown, nil 144 | } 145 | 146 | var unknownPrefix = []byte{0x6b, 0x38, 0x73, 0x00} 147 | 148 | func parseUnknown(b []byte) (*runtime.Unknown, error) { 149 | if !bytes.HasPrefix(b, unknownPrefix) { 150 | return nil, errors.New("bytes did not start with expected prefix") 151 | } 152 | 153 | var u runtime.Unknown 154 | if err := proto.Unmarshal(b[len(unknownPrefix):], &u); err != nil { 155 | return nil, err 156 | } 157 | return &u, nil 158 | } 159 | 160 | // Watch creates a watch on a resource. It takes an example Resource to 161 | // determine what endpoint to watch. 162 | // 163 | // Watch does not automatically reconnect. If a watch fails, a new watch must 164 | // be initialized. 165 | // 166 | // // Watch configmaps in the "kube-system" namespace 167 | // var configMap corev1.ConfigMap 168 | // watcher, err := client.Watch(ctx, "kube-system", &configMap) 169 | // if err != nil { 170 | // // handle error 171 | // } 172 | // defer watcher.Close() // Always close the returned watcher. 173 | // 174 | // for { 175 | // cm := new(corev1.ConfigMap) 176 | // eventType, err := watcher.Next(cm) 177 | // if err != nil { 178 | // // watcher encountered and error, exit or create a new watcher 179 | // } 180 | // fmt.Println(eventType, *cm.Metadata.Name) 181 | // } 182 | // 183 | func (c *Client) Watch(ctx context.Context, namespace string, r Resource, options ...Option) (*Watcher, error) { 184 | url, err := resourceWatchURL(c.Endpoint, namespace, r, options...) 185 | if err != nil { 186 | return nil, err 187 | } 188 | 189 | ct := contentTypeFor(r) 190 | 191 | req, err := c.newRequest(ctx, "GET", url, nil) 192 | if err != nil { 193 | return nil, err 194 | } 195 | req.Header.Set("Accept", ct) 196 | 197 | resp, err := c.client().Do(req) 198 | if err != nil { 199 | return nil, err 200 | } 201 | 202 | if resp.StatusCode/100 != 2 { 203 | body, err := ioutil.ReadAll(resp.Body) 204 | resp.Body.Close() 205 | if err != nil { 206 | return nil, err 207 | } 208 | return nil, newAPIError(resp.Header.Get("Content-Type"), resp.StatusCode, body) 209 | } 210 | 211 | if ct == contentTypePB { 212 | return &Watcher{&watcherPB{r: resp.Body}}, nil 213 | } 214 | 215 | return &Watcher{&watcherJSON{ 216 | d: json.NewDecoder(resp.Body), 217 | c: resp.Body, 218 | }}, nil 219 | } 220 | -------------------------------------------------------------------------------- /watch_test.go: -------------------------------------------------------------------------------- 1 | package k8s_test 2 | 3 | import ( 4 | "context" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/ericchiang/k8s" 9 | corev1 "github.com/ericchiang/k8s/apis/core/v1" 10 | metav1 "github.com/ericchiang/k8s/apis/meta/v1" 11 | ) 12 | 13 | // configMapJSON is used to test the JSON serialization watch. 14 | type configMapJSON struct { 15 | Metadata *metav1.ObjectMeta `json:"metadata"` 16 | Data map[string]string `json:"data"` 17 | } 18 | 19 | func (c *configMapJSON) GetMetadata() *metav1.ObjectMeta { 20 | return c.Metadata 21 | } 22 | 23 | func init() { 24 | k8s.Register("", "v1", "configmaps", true, &configMapJSON{}) 25 | } 26 | 27 | func testWatch(t *testing.T, client *k8s.Client, namespace string, newCM func() k8s.Resource, update func(cm k8s.Resource)) { 28 | w, err := client.Watch(context.TODO(), namespace, newCM()) 29 | if err != nil { 30 | t.Errorf("watch configmaps: %v", err) 31 | } 32 | defer w.Close() 33 | 34 | cm := newCM() 35 | want := func(eventType string) { 36 | got := newCM() 37 | eT, err := w.Next(got) 38 | if err != nil { 39 | t.Errorf("decode watch event: %v", err) 40 | return 41 | } 42 | if eT != eventType { 43 | t.Errorf("expected event type %q got %q", eventType, eT) 44 | } 45 | cm.GetMetadata().ResourceVersion = k8s.String("") 46 | got.GetMetadata().ResourceVersion = k8s.String("") 47 | if !reflect.DeepEqual(got, cm) { 48 | t.Errorf("configmaps didn't match") 49 | t.Errorf("want: %#v", cm) 50 | t.Errorf(" got: %#v", got) 51 | } 52 | } 53 | 54 | if err := client.Create(context.TODO(), cm); err != nil { 55 | t.Errorf("create configmap: %v", err) 56 | return 57 | } 58 | want(k8s.EventAdded) 59 | 60 | update(cm) 61 | 62 | if err := client.Update(context.TODO(), cm); err != nil { 63 | t.Errorf("update configmap: %v", err) 64 | return 65 | } 66 | want(k8s.EventModified) 67 | 68 | if err := client.Delete(context.TODO(), cm); err != nil { 69 | t.Errorf("Delete configmap: %v", err) 70 | return 71 | } 72 | want(k8s.EventDeleted) 73 | } 74 | 75 | func TestWatchConfigMapJSON(t *testing.T) { 76 | withNamespace(t, func(client *k8s.Client, namespace string) { 77 | newCM := func() k8s.Resource { 78 | return &configMapJSON{ 79 | Metadata: &metav1.ObjectMeta{ 80 | Name: k8s.String("my-configmap"), 81 | Namespace: &namespace, 82 | }, 83 | } 84 | } 85 | 86 | updateCM := func(cm k8s.Resource) { 87 | (cm.(*configMapJSON)).Data = map[string]string{"hello": "world"} 88 | } 89 | testWatch(t, client, namespace, newCM, updateCM) 90 | }) 91 | } 92 | 93 | func TestWatchConfigMapProto(t *testing.T) { 94 | withNamespace(t, func(client *k8s.Client, namespace string) { 95 | newCM := func() k8s.Resource { 96 | return &corev1.ConfigMap{ 97 | Metadata: &metav1.ObjectMeta{ 98 | Name: k8s.String("my-configmap"), 99 | Namespace: &namespace, 100 | }, 101 | } 102 | } 103 | 104 | updateCM := func(cm k8s.Resource) { 105 | (cm.(*corev1.ConfigMap)).Data = map[string]string{"hello": "world"} 106 | } 107 | testWatch(t, client, namespace, newCM, updateCM) 108 | }) 109 | } 110 | --------------------------------------------------------------------------------