From 4e73b60a32dcf19f0608d949e127fe33b6138ebb Mon Sep 17 00:00:00 2001 From: joshvanl Date: Fri, 28 Jan 2022 17:20:55 +0000 Subject: [PATCH] Adds orders apply helper function Signed-off-by: joshvanl --- internal/BUILD.bazel | 1 + internal/controller/orders/BUILD.bazel | 40 +++++++++++++ internal/controller/orders/OWNERS | 4 ++ internal/controller/orders/apply.go | 67 ++++++++++++++++++++++ internal/controller/orders/apply_test.go | 72 ++++++++++++++++++++++++ 5 files changed, 184 insertions(+) create mode 100644 internal/controller/orders/BUILD.bazel create mode 100644 internal/controller/orders/OWNERS create mode 100644 internal/controller/orders/apply.go create mode 100644 internal/controller/orders/apply_test.go diff --git a/internal/BUILD.bazel b/internal/BUILD.bazel index 6fdce9d42..9c1c77c6e 100644 --- a/internal/BUILD.bazel +++ b/internal/BUILD.bazel @@ -17,6 +17,7 @@ filegroup( "//internal/controller/certificates:all-srcs", "//internal/controller/feature:all-srcs", "//internal/controller/issuers:all-srcs", + "//internal/controller/orders:all-srcs", "//internal/ingress:all-srcs", "//internal/plugin:all-srcs", "//internal/test/paths:all-srcs", diff --git a/internal/controller/orders/BUILD.bazel b/internal/controller/orders/BUILD.bazel new file mode 100644 index 000000000..1b26df1d7 --- /dev/null +++ b/internal/controller/orders/BUILD.bazel @@ -0,0 +1,40 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["apply.go"], + importpath = "github.com/cert-manager/cert-manager/internal/controller/orders", + visibility = ["//:__subpackages__"], + deps = [ + "//pkg/apis/acme/v1:go_default_library", + "//pkg/client/clientset/versioned:go_default_library", + "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library", + "@io_k8s_apimachinery//pkg/types:go_default_library", + "@io_k8s_utils//pointer:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["apply_test.go"], + embed = [":go_default_library"], + deps = [ + "//pkg/apis/acme/v1:go_default_library", + "@com_github_google_gofuzz//:go_default_library", + "@com_github_stretchr_testify//assert:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/internal/controller/orders/OWNERS b/internal/controller/orders/OWNERS new file mode 100644 index 000000000..a846a6112 --- /dev/null +++ b/internal/controller/orders/OWNERS @@ -0,0 +1,4 @@ +filters: + "^apply(_test)?\\.go$": + required_reviewers: + - joshvanl diff --git a/internal/controller/orders/apply.go b/internal/controller/orders/apply.go new file mode 100644 index 000000000..722f5e79e --- /dev/null +++ b/internal/controller/orders/apply.go @@ -0,0 +1,67 @@ +/* +Copyright 2020 The cert-manager 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 orders + +import ( + "context" + "encoding/json" + "fmt" + + cmclient "github.com/jetstack/cert-manager/pkg/client/clientset/versioned" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + apitypes "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/pointer" + + cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1" +) + +// ApplyStatus will make a Apply API call with the given client to the order's +// status sub-resource endpoint. All data in the given Order object is dropped; +// expect for the name, namespace, and status object. The given fieldManager is +// will be used as the FieldManager in the Apply call. +// Always sets Force Apply to true. +func ApplyStatus(ctx context.Context, cl cmclient.Interface, fieldManager string, order *cmacme.Order) error { + orderData, err := serializeApplyStatus(order) + if err != nil { + return err + } + + _, err = cl.AcmeV1().Orders(order.Namespace).Patch( + ctx, order.Name, apitypes.ApplyPatchType, orderData, + metav1.PatchOptions{Force: pointer.Bool(true), FieldManager: fieldManager}, "status", + ) + + return err +} + +// serializeApplyStatus converts the given Order object in JSON. Only the name, +// namespace, and status field values will be copied and encoded into the +// serialized slice. All other fields will be left at their zero value. +// TypeMeta will be populated with the Kind "Order" and API Version +// "acme.cert-manager.io/v1" respectively. +func serializeApplyStatus(order *cmacme.Order) ([]byte, error) { + order = &cmacme.Order{ + TypeMeta: metav1.TypeMeta{Kind: cmacme.OrderKind, APIVersion: cmacme.SchemeGroupVersion.Identifier()}, + ObjectMeta: metav1.ObjectMeta{Namespace: order.Namespace, Name: order.Name}, + Status: order.Status, + } + orderData, err := json.Marshal(order) + if err != nil { + return nil, fmt.Errorf("failed to marshal order object: %w", err) + } + return orderData, nil +} diff --git a/internal/controller/orders/apply_test.go b/internal/controller/orders/apply_test.go new file mode 100644 index 000000000..407c3c2b0 --- /dev/null +++ b/internal/controller/orders/apply_test.go @@ -0,0 +1,72 @@ +/* +Copyright 2020 The cert-manager 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 orders + +import ( + "strconv" + "sync" + "testing" + + fuzz "github.com/google/gofuzz" + "github.com/stretchr/testify/assert" + + cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1" +) + +func Test_serializeApplyStatus(t *testing.T) { + const ( + expReg = `^{"kind":"Order","apiVersion":"acme.cert-manager.io/v1","metadata":{"name":"foo","namespace":"bar","creationTimestamp":null},"spec":{"request":null,"issuerRef":{"name":""}},"status":{.*}$` + expEmpty = `{"kind":"Order","apiVersion":"acme.cert-manager.io/v1","metadata":{"name":"foo","namespace":"bar","creationTimestamp":null},"spec":{"request":null,"issuerRef":{"name":""}},"status":{}}` + numJobs = 10000 + ) + + var wg sync.WaitGroup + jobs := make(chan int) + + wg.Add(numJobs) + for i := 0; i < 3; i++ { + go func() { + for j := range jobs { + t.Run("fuzz_"+strconv.Itoa(j), func(t *testing.T) { + var order cmacme.Order + fuzz.New().NilChance(0.5).Fuzz(&order) + order.Name = "foo" + order.Namespace = "bar" + + // Test regex with non-empty status. + orderData, err := serializeApplyStatus(&order) + assert.NoError(t, err) + assert.Regexp(t, expReg, string(orderData)) + + // String match on empty status. + order.Status = cmacme.OrderStatus{} + orderData, err = serializeApplyStatus(&order) + assert.NoError(t, err) + assert.Equal(t, expEmpty, string(orderData)) + + wg.Done() + }) + } + }() + } + + for i := 0; i < numJobs; i++ { + jobs <- i + } + close(jobs) + wg.Wait() +}