Skip to content

Commit

Permalink
Comments
Browse files Browse the repository at this point in the history
  • Loading branch information
Jefftree committed Sep 13, 2022
1 parent 3a0b793 commit ab9ceff
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 37 deletions.
36 changes: 31 additions & 5 deletions examples/applyconfig/api/v1/ac/zz_generated.applyconfigurations.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion examples/applyconfig/api/v1/foo.go
@@ -1,4 +1,6 @@
//go:generate controller-gen object crd paths="." output:dir="."

// +kubebuilder:ac:generate=true
// +groupName=applytest.kubebuilder.io
// +versionName=v1
//go:generate $GOPATH/src/sigs.k8s.io/controller-tools/controller-gen apply paths="./..."
Expand All @@ -9,9 +11,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +kubebuilder:resource:singular=foo
// +kubebuilder:object:generate=true
// +kubebuilder:object:root=true
// +kubebuilder:ac:root=true
type Foo struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Expand Down
12 changes: 2 additions & 10 deletions examples/applyconfig/main.go
Expand Up @@ -19,8 +19,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/scheme"

metav1ac "k8s.io/client-go/applyconfigurations/meta/v1"
)

var sch *runtime.Scheme
Expand Down Expand Up @@ -108,17 +106,11 @@ func runMain() int {
fooObj := &v1.Foo{}

// To use the SSA typed client, use the ApplyConfiguration generated types instead of the types defined in v1
applyConfig1 := (&ac.FooApplyConfiguration{
TypeMetaApplyConfiguration: *metav1ac.TypeMeta().WithKind("Foo").WithAPIVersion("applytest.kubebuilder.io/v1"),
ObjectMetaApplyConfiguration: metav1ac.ObjectMeta().WithName("acdefault").WithNamespace("default"),
}).WithNonNullableField("value1")
applyConfig1 := ac.Foo().WithName("acdefault").WithNamespace("default").WithNonNullableField("value1")

// Without ApplyConfiguration, NonNullableField is not an optional field so it must be specified if Foo{} was used instead of the ApplyConfiguration
// The ApplyConfiguration uses pointers for all objects, so omitting a field is equivalent to saying that it is not of interest in this Apply
applyConfig2 := (&ac.FooApplyConfiguration{
TypeMetaApplyConfiguration: *metav1ac.TypeMeta().WithKind("Foo").WithAPIVersion("applytest.kubebuilder.io/v1"),
ObjectMetaApplyConfiguration: metav1ac.ObjectMeta().WithName("acdefault").WithNamespace("default"),
}).WithNullableField("value2")
applyConfig2 := ac.Foo().WithName("acdefault").WithNamespace("default").WithNullableField("value2")

if err := cl.Patch(context.TODO(), fooObj, client.ApplyFrom(applyConfig1), owner); err != nil {
panic(err)
Expand Down
8 changes: 8 additions & 0 deletions pkg/client/object.go
Expand Up @@ -75,3 +75,11 @@ type ObjectList interface {
metav1.ListInterface
runtime.Object
}

// ApplyConfigurationObject is a special object used for server side apply. It is a
// Kubernetes object with all fields set as pointers to distinguish between nil value and empty.
// It implements the getters for Name and Namespace for use with with ApplyPatch.
type ApplyConfigurationObject interface {
GetNamespace() string
GetName() string
}
28 changes: 7 additions & 21 deletions pkg/client/patch.go
Expand Up @@ -18,7 +18,6 @@ package client

import (
"fmt"
"reflect"

jsonpatch "github.com/evanphx/json-patch/v5"
"k8s.io/apimachinery/pkg/types"
Expand Down Expand Up @@ -215,7 +214,7 @@ func (p applyPatch) Data(obj Object) ([]byte, error) {

// applyFromPatch uses server-side-apply with a ApplyConfiguration object.
type applyFromPatch struct {
patch interface{}
patch ApplyConfigurationObject
}

// Type implements Patch.
Expand All @@ -224,25 +223,12 @@ func (p applyFromPatch) Type() types.PatchType {
}

// setNameNamespace sets name and namespace for the returned object if an ApplyConfiguration was supplied.
func setNameNamespace(obj Object, ac interface{}) error {
if !reflect.ValueOf(ac).Elem().IsValid() || reflect.ValueOf(ac).Elem().Kind() != reflect.Struct {
return fmt.Errorf("ApplyConfiguration is not a valid struct")
func setNameNamespace(obj Object, ac ApplyConfigurationObject) error {
if ac.GetName() == "" {
return fmt.Errorf("ApplyConfiguration must have a non-empty Name")
}
// Since Name and Namespace are embedded objects, nil checks must be
// performed against the embedded ObjectMeta object first.
if reflect.ValueOf(ac).Elem().FieldByName("ObjectMetaApplyConfiguration").IsNil() {
return fmt.Errorf("ApplyConfiguration must include ObjectMetaApplyConfiguration")
}
if reflect.ValueOf(ac).Elem().FieldByName("Name").Elem().IsZero() {
return fmt.Errorf("ApplyConfiguration must have a non-empty Name in ObjectMetaApplyConfiguration")
}
obj.SetName(reflect.ValueOf(ac).Elem().FieldByName("Name").Elem().String())
// Only set the namespace if the object provides a namespace. This value
// is omitted for cluster scoped objects.
if !reflect.ValueOf(ac).Elem().FieldByName("Namespace").Elem().IsZero() {
obj.SetNamespace(reflect.ValueOf(ac).Elem().FieldByName("Namespace").Elem().String())
}

obj.SetName(ac.GetName())
obj.SetNamespace(ac.GetNamespace())
return nil
}

Expand All @@ -261,6 +247,6 @@ func (p applyFromPatch) Data(o Object) ([]byte, error) {
}

// ApplyFrom creates an applyFromPatch object with the provided ApplyConfiguration.
func ApplyFrom(ac interface{}) Patch {
func ApplyFrom(ac ApplyConfigurationObject) Patch {
return applyFromPatch{patch: ac}
}

0 comments on commit ab9ceff

Please sign in to comment.