mirror of
https://github.com/kubernetes/client-go.git
synced 2025-07-08 04:28:56 +00:00
ThirdPartyResource client-go example: added TPR controller example, code cleanup and integration test
Kubernetes-commit: a6c97715ed7e24581dd9a02a256d57e56b70ac44
This commit is contained in:
parent
bb2e182124
commit
8d49905c48
@ -6,7 +6,6 @@ load(
|
|||||||
"@io_bazel_rules_go//go:def.bzl",
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
"go_binary",
|
"go_binary",
|
||||||
"go_library",
|
"go_library",
|
||||||
"go_test",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
go_binary(
|
go_binary(
|
||||||
@ -15,34 +14,18 @@ go_binary(
|
|||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
)
|
)
|
||||||
|
|
||||||
go_test(
|
|
||||||
name = "go_default_test",
|
|
||||||
srcs = ["types_test.go"],
|
|
||||||
library = ":go_default_library",
|
|
||||||
tags = ["automanaged"],
|
|
||||||
deps = [
|
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = ["main.go"],
|
||||||
"main.go",
|
|
||||||
"types.go",
|
|
||||||
],
|
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
deps = [
|
deps = [
|
||||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//vendor/k8s.io/client-go/examples/third-party-resources/apis/tpr/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//vendor/k8s.io/client-go/examples/third-party-resources/client:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
"//vendor/k8s.io/client-go/examples/third-party-resources/controller:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
|
||||||
"//vendor/k8s.io/client-go/pkg/api/v1:go_default_library",
|
"//vendor/k8s.io/client-go/pkg/api/v1:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/pkg/apis/extensions/v1beta1:go_default_library",
|
|
||||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||||
],
|
],
|
||||||
|
@ -4,6 +4,7 @@ This particular example demonstrates how to perform basic operations such as:
|
|||||||
|
|
||||||
* How to register a new ThirdPartyResource (custom Resource type)
|
* How to register a new ThirdPartyResource (custom Resource type)
|
||||||
* How to create/get/list instances of your new Resource type (update/delete/etc work as well but are not demonstrated)
|
* How to create/get/list instances of your new Resource type (update/delete/etc work as well but are not demonstrated)
|
||||||
|
* How to setup a controller on Resource handling create/update/delete events
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
|
34
examples/third-party-resources/apis/tpr/v1/BUILD
Normal file
34
examples/third-party-resources/apis/tpr/v1/BUILD
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
load(
|
||||||
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
|
"go_library",
|
||||||
|
"go_test",
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["types_test.go"],
|
||||||
|
library = ":go_default_library",
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = [
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"register.go",
|
||||||
|
"types.go",
|
||||||
|
],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = [
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
56
examples/third-party-resources/apis/tpr/v1/register.go
Normal file
56
examples/third-party-resources/apis/tpr/v1/register.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
|
||||||
|
AddToScheme = SchemeBuilder.AddToScheme
|
||||||
|
)
|
||||||
|
|
||||||
|
// GroupName is the group name use in this package
|
||||||
|
const GroupName = "tpr.client-go.k8s.io"
|
||||||
|
|
||||||
|
// SchemeGroupVersion is group version used to register these objects
|
||||||
|
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}
|
||||||
|
|
||||||
|
// Resource takes an unqualified resource and returns a Group qualified GroupResource
|
||||||
|
func Resource(resource string) schema.GroupResource {
|
||||||
|
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// We only register manually written functions here. The registration of the
|
||||||
|
// generated functions takes place in the generated files. The separation
|
||||||
|
// makes the code compile even when the generated files are missing.
|
||||||
|
SchemeBuilder.Register(addKnownTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds the list of known types to api.Scheme.
|
||||||
|
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||||
|
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||||
|
&Example{},
|
||||||
|
&ExampleList{},
|
||||||
|
)
|
||||||
|
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||||
|
return nil
|
||||||
|
}
|
53
examples/third-party-resources/apis/tpr/v1/types.go
Normal file
53
examples/third-party-resources/apis/tpr/v1/types.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ExampleResourcePlural = "examples"
|
||||||
|
|
||||||
|
type Example struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata"`
|
||||||
|
Spec ExampleSpec `json:"spec"`
|
||||||
|
Status ExampleStatus `json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExampleSpec struct {
|
||||||
|
Foo string `json:"foo"`
|
||||||
|
Bar bool `json:"bar"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExampleStatus struct {
|
||||||
|
State ExampleState `json:"state,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExampleState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ExampleStateCreated ExampleState = "Created"
|
||||||
|
ExampleStateProcessed ExampleState = "Processed"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExampleList struct {
|
||||||
|
metav1.TypeMeta `json:",inline"`
|
||||||
|
metav1.ListMeta `json:"metadata"`
|
||||||
|
Items []Example `json:"items"`
|
||||||
|
}
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package main
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
29
examples/third-party-resources/client/BUILD
Normal file
29
examples/third-party-resources/client/BUILD
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
load(
|
||||||
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
|
"go_library",
|
||||||
|
)
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = [
|
||||||
|
"client.go",
|
||||||
|
"tpr.go",
|
||||||
|
],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = [
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/examples/third-party-resources/apis/tpr/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/pkg/api/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/pkg/apis/extensions/v1beta1:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
45
examples/third-party-resources/client/client.go
Normal file
45
examples/third-party-resources/client/client.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
|
||||||
|
tprv1 "k8s.io/client-go/examples/third-party-resources/apis/tpr/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewClient(cfg *rest.Config) (*rest.RESTClient, *runtime.Scheme, error) {
|
||||||
|
scheme := runtime.NewScheme()
|
||||||
|
if err := tprv1.AddToScheme(scheme); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
config := *cfg
|
||||||
|
config.GroupVersion = &tprv1.SchemeGroupVersion
|
||||||
|
config.APIPath = "/apis"
|
||||||
|
config.ContentType = runtime.ContentTypeJSON
|
||||||
|
config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: serializer.NewCodecFactory(scheme)}
|
||||||
|
|
||||||
|
client, err := rest.RESTClientFor(&config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return client, scheme, nil
|
||||||
|
}
|
76
examples/third-party-resources/client/tpr.go
Normal file
76
examples/third-party-resources/client/tpr.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
tprv1 "k8s.io/client-go/examples/third-party-resources/apis/tpr/v1"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
apiv1 "k8s.io/client-go/pkg/api/v1"
|
||||||
|
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
// Uncomment the following line to load the gcp plugin (only required to authenticate against GKE clusters).
|
||||||
|
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateTPR(clientset kubernetes.Interface) error {
|
||||||
|
tpr := &v1beta1.ThirdPartyResource{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "example." + tprv1.GroupName,
|
||||||
|
},
|
||||||
|
Versions: []v1beta1.APIVersion{
|
||||||
|
{Name: tprv1.SchemeGroupVersion.Version},
|
||||||
|
},
|
||||||
|
Description: "An Example ThirdPartyResource",
|
||||||
|
}
|
||||||
|
_, err := clientset.ExtensionsV1beta1().ThirdPartyResources().Create(tpr)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func WaitForExampleResource(exampleClient *rest.RESTClient) error {
|
||||||
|
return wait.Poll(100*time.Millisecond, 60*time.Second, func() (bool, error) {
|
||||||
|
_, err := exampleClient.Get().Namespace(apiv1.NamespaceDefault).Resource(tprv1.ExampleResourcePlural).DoRaw()
|
||||||
|
if err == nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if apierrors.IsNotFound(err) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func WaitForExampleInstanceProcessed(exampleClient *rest.RESTClient, name string) error {
|
||||||
|
return wait.Poll(100*time.Millisecond, 10*time.Second, func() (bool, error) {
|
||||||
|
var example tprv1.Example
|
||||||
|
err := exampleClient.Get().
|
||||||
|
Resource(tprv1.ExampleResourcePlural).
|
||||||
|
Namespace(apiv1.NamespaceDefault).
|
||||||
|
Name(name).
|
||||||
|
Do().Into(&example)
|
||||||
|
|
||||||
|
if err == nil && example.Status.State == tprv1.ExampleStateProcessed {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, err
|
||||||
|
})
|
||||||
|
}
|
22
examples/third-party-resources/controller/BUILD
Normal file
22
examples/third-party-resources/controller/BUILD
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
load(
|
||||||
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
|
"go_library",
|
||||||
|
)
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["controller.go"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = [
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/examples/third-party-resources/apis/tpr/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/pkg/api/v1:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
126
examples/third-party-resources/controller/controller.go
Normal file
126
examples/third-party-resources/controller/controller.go
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
apiv1 "k8s.io/client-go/pkg/api/v1"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
|
||||||
|
tprv1 "k8s.io/client-go/examples/third-party-resources/apis/tpr/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Watcher is an example of watching on resource create/update/delete events
|
||||||
|
type ExampleController struct {
|
||||||
|
ExampleClient *rest.RESTClient
|
||||||
|
ExampleScheme *runtime.Scheme
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run starts an Example resource controller
|
||||||
|
func (c *ExampleController) Run(ctx context.Context) error {
|
||||||
|
fmt.Print("Watch Example objects\n")
|
||||||
|
|
||||||
|
// Watch Example objects
|
||||||
|
_, err := c.watchExamples(ctx)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to register watch for Example resource: %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
<-ctx.Done()
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ExampleController) watchExamples(ctx context.Context) (cache.Controller, error) {
|
||||||
|
source := cache.NewListWatchFromClient(
|
||||||
|
c.ExampleClient,
|
||||||
|
tprv1.ExampleResourcePlural,
|
||||||
|
apiv1.NamespaceAll,
|
||||||
|
fields.Everything())
|
||||||
|
|
||||||
|
_, controller := cache.NewInformer(
|
||||||
|
source,
|
||||||
|
|
||||||
|
// The object type.
|
||||||
|
&tprv1.Example{},
|
||||||
|
|
||||||
|
// resyncPeriod
|
||||||
|
// Every resyncPeriod, all resources in the cache will retrigger events.
|
||||||
|
// Set to 0 to disable the resync.
|
||||||
|
0,
|
||||||
|
|
||||||
|
// Your custom resource event handlers.
|
||||||
|
cache.ResourceEventHandlerFuncs{
|
||||||
|
AddFunc: c.onAdd,
|
||||||
|
UpdateFunc: c.onUpdate,
|
||||||
|
DeleteFunc: c.onDelete,
|
||||||
|
})
|
||||||
|
|
||||||
|
go controller.Run(ctx.Done())
|
||||||
|
return controller, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ExampleController) onAdd(obj interface{}) {
|
||||||
|
example := obj.(*tprv1.Example)
|
||||||
|
fmt.Printf("[CONTROLLER] OnAdd %s\n", example.ObjectMeta.SelfLink)
|
||||||
|
|
||||||
|
// NEVER modify objects from the store. It's a read-only, local cache.
|
||||||
|
// You can use exampleScheme.Copy() to make a deep copy of original object and modify this copy
|
||||||
|
// Or create a copy manually for better performance
|
||||||
|
copyObj, err := c.ExampleScheme.Copy(example)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ERROR creating a deep copy of example object: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
exampleCopy := copyObj.(*tprv1.Example)
|
||||||
|
exampleCopy.Status = tprv1.ExampleStatus{
|
||||||
|
State: tprv1.ExampleStateProcessed,
|
||||||
|
Message: "Successfully processed by controller",
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.ExampleClient.Put().
|
||||||
|
Name(example.ObjectMeta.Name).
|
||||||
|
Namespace(example.ObjectMeta.Namespace).
|
||||||
|
Resource(tprv1.ExampleResourcePlural).
|
||||||
|
Body(exampleCopy).
|
||||||
|
Do().
|
||||||
|
Error()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ERROR updating status: %v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("UPDATED status: %#v\n", exampleCopy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ExampleController) onUpdate(oldObj, newObj interface{}) {
|
||||||
|
oldExample := oldObj.(*tprv1.Example)
|
||||||
|
newExample := newObj.(*tprv1.Example)
|
||||||
|
fmt.Printf("[CONTROLLER] OnUpdate oldObj: %s\n", oldExample.ObjectMeta.SelfLink)
|
||||||
|
fmt.Printf("[CONTROLLER] OnUpdate newObj: %s\n", newExample.ObjectMeta.SelfLink)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ExampleController) onDelete(obj interface{}) {
|
||||||
|
example := obj.(*tprv1.Example)
|
||||||
|
fmt.Printf("[CONTROLLER] OnDelete %s\n", example.ObjectMeta.SelfLink)
|
||||||
|
}
|
@ -18,22 +18,23 @@ limitations under the License.
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/kubernetes/scheme"
|
apiv1 "k8s.io/client-go/pkg/api/v1"
|
||||||
"k8s.io/client-go/pkg/api/v1"
|
|
||||||
"k8s.io/client-go/pkg/apis/extensions/v1beta1"
|
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
|
|
||||||
// Uncomment the following line to load the gcp plugin (only required to authenticate against GKE clusters).
|
// Uncomment the following line to load the gcp plugin (only required to authenticate against GKE clusters).
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||||
|
|
||||||
|
tprv1 "k8s.io/client-go/examples/third-party-resources/apis/tpr/v1"
|
||||||
|
exampleclient "k8s.io/client-go/examples/third-party-resources/client"
|
||||||
|
examplecontroller "k8s.io/client-go/examples/third-party-resources/controller"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -52,83 +53,71 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// initialize third party resource if it does not exist
|
// initialize third party resource if it does not exist
|
||||||
tpr, err := clientset.ExtensionsV1beta1().ThirdPartyResources().Get("example.k8s.io", metav1.GetOptions{})
|
err = exampleclient.CreateTPR(clientset)
|
||||||
if err != nil {
|
if err != nil && !apierrors.IsAlreadyExists(err) {
|
||||||
if errors.IsNotFound(err) {
|
|
||||||
tpr := &v1beta1.ThirdPartyResource{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "example.k8s.io",
|
|
||||||
},
|
|
||||||
Versions: []v1beta1.APIVersion{
|
|
||||||
{Name: "v1"},
|
|
||||||
},
|
|
||||||
Description: "An Example ThirdPartyResource",
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := clientset.ExtensionsV1beta1().ThirdPartyResources().Create(tpr)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
fmt.Printf("CREATED: %#v\nFROM: %#v\n", result, tpr)
|
|
||||||
} else {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Printf("SKIPPING: already exists %#v\n", tpr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// make a new config for our extension's API group, using the first config as a baseline
|
// make a new config for our extension's API group, using the first config as a baseline
|
||||||
var tprconfig *rest.Config
|
exampleClient, exampleScheme, err := exampleclient.NewClient(config)
|
||||||
tprconfig = config
|
|
||||||
configureClient(tprconfig)
|
|
||||||
|
|
||||||
tprclient, err := rest.RESTClientFor(tprconfig)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var example Example
|
// wait until TPR gets processed
|
||||||
|
err = exampleclient.WaitForExampleResource(exampleClient)
|
||||||
err = tprclient.Get().
|
|
||||||
Resource("examples").
|
|
||||||
Namespace(v1.NamespaceDefault).
|
|
||||||
Name("example1").
|
|
||||||
Do().Into(&example)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.IsNotFound(err) {
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// start a controller on instances of our TPR
|
||||||
|
controller := examplecontroller.ExampleController{
|
||||||
|
ExampleClient: exampleClient,
|
||||||
|
ExampleScheme: exampleScheme,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||||
|
defer cancelFunc()
|
||||||
|
go controller.Run(ctx)
|
||||||
|
|
||||||
// Create an instance of our TPR
|
// Create an instance of our TPR
|
||||||
example := &Example{
|
example := &tprv1.Example{
|
||||||
Metadata: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "example1",
|
Name: "example1",
|
||||||
},
|
},
|
||||||
Spec: ExampleSpec{
|
Spec: tprv1.ExampleSpec{
|
||||||
Foo: "hello",
|
Foo: "hello",
|
||||||
Bar: true,
|
Bar: true,
|
||||||
},
|
},
|
||||||
|
Status: tprv1.ExampleStatus{
|
||||||
|
State: tprv1.ExampleStateCreated,
|
||||||
|
Message: "Created, not processed yet",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
var result tprv1.Example
|
||||||
var result Example
|
err = exampleClient.Post().
|
||||||
err = tprclient.Post().
|
Resource(tprv1.ExampleResourcePlural).
|
||||||
Resource("examples").
|
Namespace(apiv1.NamespaceDefault).
|
||||||
Namespace(v1.NamespaceDefault).
|
|
||||||
Body(example).
|
Body(example).
|
||||||
Do().Into(&result)
|
Do().Into(&result)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Printf("CREATED: %#v\n", result)
|
||||||
|
} else if apierrors.IsAlreadyExists(err) {
|
||||||
|
fmt.Printf("ALREADY EXISTS: %#v\n", result)
|
||||||
|
} else {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Poll until Example object is handled by controller and gets status updated to "Processed"
|
||||||
|
err = exampleclient.WaitForExampleInstanceProcessed(exampleClient, "example1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
fmt.Printf("CREATED: %#v\n", result)
|
fmt.Print("PROCESSED\n")
|
||||||
} else {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fmt.Printf("GET: %#v\n", example)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch a list of our TPRs
|
// Fetch a list of our TPRs
|
||||||
exampleList := ExampleList{}
|
exampleList := tprv1.ExampleList{}
|
||||||
err = tprclient.Get().Resource("examples").Do().Into(&exampleList)
|
err = exampleClient.Get().Resource(tprv1.ExampleResourcePlural).Do().Into(&exampleList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -141,27 +130,3 @@ func buildConfig(kubeconfig string) (*rest.Config, error) {
|
|||||||
}
|
}
|
||||||
return rest.InClusterConfig()
|
return rest.InClusterConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureClient(config *rest.Config) {
|
|
||||||
groupversion := schema.GroupVersion{
|
|
||||||
Group: "k8s.io",
|
|
||||||
Version: "v1",
|
|
||||||
}
|
|
||||||
|
|
||||||
config.GroupVersion = &groupversion
|
|
||||||
config.APIPath = "/apis"
|
|
||||||
config.ContentType = runtime.ContentTypeJSON
|
|
||||||
config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
|
|
||||||
|
|
||||||
schemeBuilder := runtime.NewSchemeBuilder(
|
|
||||||
func(scheme *runtime.Scheme) error {
|
|
||||||
scheme.AddKnownTypes(
|
|
||||||
groupversion,
|
|
||||||
&Example{},
|
|
||||||
&ExampleList{},
|
|
||||||
)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
metav1.AddToGroupVersion(scheme.Scheme, groupversion)
|
|
||||||
schemeBuilder.AddToScheme(scheme.Scheme)
|
|
||||||
}
|
|
||||||
|
@ -1,92 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2017 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ExampleSpec struct {
|
|
||||||
Foo string `json:"foo"`
|
|
||||||
Bar bool `json:"bar"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Example struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
Metadata metav1.ObjectMeta `json:"metadata"`
|
|
||||||
|
|
||||||
Spec ExampleSpec `json:"spec"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExampleList struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
Metadata metav1.ListMeta `json:"metadata"`
|
|
||||||
|
|
||||||
Items []Example `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Required to satisfy Object interface
|
|
||||||
func (e *Example) GetObjectKind() schema.ObjectKind {
|
|
||||||
return &e.TypeMeta
|
|
||||||
}
|
|
||||||
|
|
||||||
// Required to satisfy ObjectMetaAccessor interface
|
|
||||||
func (e *Example) GetObjectMeta() metav1.Object {
|
|
||||||
return &e.Metadata
|
|
||||||
}
|
|
||||||
|
|
||||||
// Required to satisfy Object interface
|
|
||||||
func (el *ExampleList) GetObjectKind() schema.ObjectKind {
|
|
||||||
return &el.TypeMeta
|
|
||||||
}
|
|
||||||
|
|
||||||
// Required to satisfy ListMetaAccessor interface
|
|
||||||
func (el *ExampleList) GetListMeta() metav1.List {
|
|
||||||
return &el.Metadata
|
|
||||||
}
|
|
||||||
|
|
||||||
// The code below is used only to work around a known problem with third-party
|
|
||||||
// resources and ugorji. If/when these issues are resolved, the code below
|
|
||||||
// should no longer be required.
|
|
||||||
|
|
||||||
type ExampleListCopy ExampleList
|
|
||||||
type ExampleCopy Example
|
|
||||||
|
|
||||||
func (e *Example) UnmarshalJSON(data []byte) error {
|
|
||||||
tmp := ExampleCopy{}
|
|
||||||
err := json.Unmarshal(data, &tmp)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tmp2 := Example(tmp)
|
|
||||||
*e = tmp2
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (el *ExampleList) UnmarshalJSON(data []byte) error {
|
|
||||||
tmp := ExampleListCopy{}
|
|
||||||
err := json.Unmarshal(data, &tmp)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tmp2 := ExampleList(tmp)
|
|
||||||
*el = tmp2
|
|
||||||
return nil
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user