semantics of Custom Resources • From JSON blob store to schema based storage OpenAPIv3Schema: { properties: { foo: {} } } • Example CR: { "foo": 1, "bar": 2 } → { "foo": 1 } Opt-in in CRD v1beta1 Mandatory in GA
group: kubecon.io version: v1 scope: Namespaced names: plural: sessions singular: session kind: Session # shortNames: # - talks must match the kind: - usually capital singular - like the Go type the resource: - usually lower-case singular - in http path
sessions.kubecon.io.yaml ... and then watch status.conditions["Established"]. Conditions: → NamesAccepted → Established = no name conflicts = CRD is served* * There is a race ! – to be fixed in #63068. Better wait 5 seconds in ≤1.10.
GET https://localhost:6443/apis • I0429 21:17:53.135811 66743 round_trippers.go:383] GET https://localhost:6443/apis/kubecon.io/v1 • I0429 21:17:53.138353 66743 round_trippers.go:383] GET https://localhost:6443/apis/kubecon.io/v1/namespaces/default/sessions No resources found. sessions → kind Session resource sessions discovery LIST note: we also support "shortNames" in API group kubecon.io/v1 We call this "REST mapping"
eu2018 spec: type: deepdive title: "SIG API Machinery Deep Dive" capacity: 42 status: attendees: 23 conditions: - lastTransitionTime: 2018-05-04T12:47:54Z status: "True" type: Started Recommended to follow the spec+status pattern. Important for /status subresource.
sessions/api-machinery --v=7 I0429 22:33:03.083150 74535 round_trippers.go:383] GET https://localhost:6443/apis/kubecon.io/v1/namespaces/eu2018/sessions/api- machinery/scale I0429 22:33:03.083725 74535 round_trippers.go:408] Response Status: 404 Not Found in 0 milliseconds We call this "subresource /scale".
semantics of Custom Resources • From JSON blob store to schema based storage OpenAPIv3Schema: { properties: { foo: {} } } • Example CR: { "foo": 1, "bar": 2 } → { "foo": 1 } Opt-in in CRD v1beta1 Mandatory in GA
SessionSpec `json:"spec"` Status SessionStatus `json:"status"` } type SessionSpec struct { Type SessionType `json:"type"` Capacity int `json:"capacity"` Title string `json:"title"` } type SessionStatus struct { Attendees int `json:"attendees,omitempty"` Conditions []SessionCondition `json:"conditions,omitempty"` } Towards a controller: Golang types pkg/apis/kubecon.io/v1/types.go doc.go: // +k8s:deepcopy-gen=package package v1
`json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` Spec SessionSpec `json:"spec"` Status SessionStatus `json:"status"` } type SessionSpec struct { Type SessionType `json:"type"` Capacity int `json:"capacity"` Title string `json:"title"` } type SessionStatus struct { Attendees int `json:"attendees,omitempty"` Conditions []SessionCondition `json:"conditions,omitempty"` Towards a controller: Golang types pkg/apis/kubecon.io/v1/types.go doc.go: // +k8s:deepcopy-gen=package package v1 More info about code generation in my OpenShift blog post Code Generation for CustomResources
"kubecon.io" var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"} func Kind(kind string) schema.GroupKind { return SchemeGroupVersion.WithKind(kind).GroupKind() } func Resource(resource string) schema.GroupResource { return SchemeGroupVersion.WithResource(resource).GroupResource() } var SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) var AddToScheme = SchemeBuilder.AddToScheme // Adds the list of known types to Scheme. func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &Session{}, &SessionList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil } hint: copy from some other API group
"kubecon.io:v1,v2,v3 someothergroup.io:v1" \ --output-base "$GOPATH/src" \ --go-header-file hack/boilerplate.go.txt Usually put into hack/update-codegen.sh (compare k8s.io/sample-controller). all = deepcopy,defaulter,client,lister,informer doc.go for crazy group and package names: // +groupName=example.test.crd.code-generator.k8s.io // +groupGoName=SecondExample Alternative, wrapping this: kubebuilder