Skip to content

Commit

Permalink
Merge pull request #40 from ecordell/rmcr
Browse files Browse the repository at this point in the history
Remove dependency on controller-runtime
  • Loading branch information
ecordell authored Aug 17, 2023
2 parents 6005d29 + 5547262 commit a8e0815
Show file tree
Hide file tree
Showing 21 changed files with 2,092 additions and 306 deletions.
25 changes: 21 additions & 4 deletions .github/workflows/build-test.yaml
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
---
name: "Build & Test"
on: # yamllint disable-line rule:truthy
on: # yamllint disable-line rule:truthy
push:
branches:
- "main"
pull_request:
branches:
- "*"
env:
GO_VERSION: "~1.20.7"
jobs:
unit:
name: "Unit"
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v3"
- uses: "actions/setup-go@v3"
- uses: "authzed/actions/setup-go@main"
with:
go-version: "~1.20"
- uses: "authzed/actions/go-test@main"
go-version: "${{ env.GO_VERSION }}"
- uses: "magefile/mage-action@v2"
with:
version: "latest"
args: "test:unit"
integration:
name: "integration"
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v3"
- uses: "authzed/actions/setup-go@main"
with:
go-version: "${{ env.GO_VERSION }}"
- uses: "magefile/mage-action@v2"
with:
version: "latest"
args: "test:integration"
23 changes: 17 additions & 6 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,34 @@ on: # yamllint disable-line rule:truthy
- "main"
pull_request:
branches: ["*"]
env:
GO_VERSION: "~1.20.7"
jobs:
go-lint:
name: "Lint Go"
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v3"
- uses: "actions/setup-go@v3"
- uses: "authzed/actions/setup-go@main"
with:
go-version: "~1.20"
- uses: "authzed/actions/gofumpt@main"
- uses: "authzed/actions/go-generate@main"
go-version: "${{ env.GO_VERSION }}"
- uses: "magefile/mage-action@v2"
with:
version: "latest"
args: "lint:go"
# golangci-lint is kept out of the module for now, since it needs to be
# run from root and would pollute the module's dependencies
- uses: "authzed/actions/golangci-lint@main"

extra-lint:
name: "Lint YAML & Markdown"
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v3"
- uses: "authzed/actions/yaml-lint@main"
- uses: "authzed/actions/markdown-lint@main"
- uses: "authzed/actions/setup-go@main"
with:
go-version: "${{ env.GO_VERSION }}"
- uses: "magefile/mage-action@v2"
with:
version: "latest"
args: "lint:extra"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.kubeconfig
110 changes: 100 additions & 10 deletions bootstrap/crds.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
package bootstrap

import (
"context"
"fmt"
"io/fs"
"path"
"time"

v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/discovery"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"k8s.io/client-go/util/retry"
)

const (
Expand All @@ -18,15 +26,21 @@ const (
)

// CRD installs the CRDs in the filesystem into the kube cluster configured by the rest config.
// Deprecated: Use CRDs instead.
func CRD(restConfig *rest.Config, crdFS fs.ReadDirFS, dir string) error {
crds := make([]*v1.CustomResourceDefinition, 0)
return CRDs(context.Background(), restConfig, crdFS, dir)
}

// CRDs installs the CRDs in the filesystem into the kube cluster configured by the rest config.
func CRDs(ctx context.Context, restConfig *rest.Config, crdFS fs.ReadDirFS, dir string) error {
crds := make([]*apiextensionsv1.CustomResourceDefinition, 0)

crdFiles, err := crdFS.ReadDir(dir)
if err != nil {
return err
}
for _, crdFile := range crdFiles {
var crd v1.CustomResourceDefinition
var crd apiextensionsv1.CustomResourceDefinition
file, err := crdFS.Open(path.Join(dir, crdFile.Name()))
if err != nil {
return err
Expand All @@ -37,11 +51,87 @@ func CRD(restConfig *rest.Config, crdFS fs.ReadDirFS, dir string) error {
crds = append(crds, &crd)
}

_, err = envtest.InstallCRDs(restConfig, envtest.CRDInstallOptions{
CRDs: crds,
MaxTime: maxCRDInstallTime,
PollInterval: crdInstallPollInterval,
CleanUpAfterUse: false,
})
if err := createCRDs(ctx, restConfig, crds); err != nil {
return err
}

if err := waitForDiscovery(ctx, restConfig, crds); err != nil {
return err
}

return err
}

// createCRDs creates (or updates) CRDs in the cluster
func createCRDs(ctx context.Context, config *rest.Config, crds []*apiextensionsv1.CustomResourceDefinition) error {
c, err := clientset.NewForConfig(config)
if err != nil {
return err
}
crdClient := c.ApiextensionsV1().CustomResourceDefinitions()
for _, crd := range crds {
crd := crd
_, err = crdClient.Get(ctx, crd.GetName(), metav1.GetOptions{})
if k8serrors.IsNotFound(err) {
if _, err := crdClient.Create(ctx, crd, metav1.CreateOptions{}); err != nil {
return fmt.Errorf("unable to create CRD %q: %w", crd.Name, err)
}
continue
}
if err != nil {
return fmt.Errorf("failed when fetching CRD %q: %w", crd.Name, err)
}

if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
got, err := crdClient.Get(ctx, crd.Name, metav1.GetOptions{})
if err != nil {
return err
}
crd.SetResourceVersion(got.GetResourceVersion())
_, err = crdClient.Update(ctx, crd, metav1.UpdateOptions{})
return err
}); err != nil {
return err
}
}
return nil
}

func waitForDiscovery(ctx context.Context, config *rest.Config, crds []*apiextensionsv1.CustomResourceDefinition) error {
gvrs := map[schema.GroupVersionResource]struct{}{}
for _, crd := range crds {
for _, version := range crd.Spec.Versions {
if !version.Served {
continue
}
gvrs[schema.GroupVersionResource{
Group: crd.Spec.Group,
Version: version.Name,
Resource: crd.Spec.Names.Plural,
}] = struct{}{}
}
}
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
if err != nil {
return err
}

return wait.PollUntilContextTimeout(ctx, crdInstallPollInterval, maxCRDInstallTime, true, func(ctx context.Context) (done bool, err error) {
_, serverGVRs, err := discoveryClient.ServerGroupsAndResources()
if err != nil {
return false, nil
}

for _, gv := range serverGVRs {
for _, r := range gv.APIResources {
delete(gvrs, schema.GroupVersionResource{
Group: r.Group,
Version: r.Version,
Resource: r.Name,
})
}
}

return len(gvrs) == 0, nil
})
}
41 changes: 41 additions & 0 deletions bootstrap/crds_e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//go:build e2e

package bootstrap

import (
"context"
"testing"

"github.com/stretchr/testify/require"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/cli-runtime/pkg/genericclioptions"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/utils/pointer"
)

func TestCRD(t *testing.T) {
opts := genericclioptions.NewConfigFlags(true)
opts.KubeConfig = pointer.String("../controller-idioms-e2e.kubeconfig")
factory := cmdutil.NewFactory(opts)
restConfig, err := factory.ToRESTConfig()
require.NoError(t, err)

// ensure CRDs
require.NoError(t, CRDs(context.Background(), restConfig, crdFS, "example"))

// create an object
client, err := factory.DynamicClient()
require.NoError(t, err)
_, err = client.Resource(schema.GroupVersionResource{
Group: "example.com",
Version: "v1",
Resource: "mytypes",
}).Namespace("default").Create(context.Background(), &unstructured.Unstructured{Object: map[string]any{
"kind": "MyType",
"apiVersion": "example.com/v1",
"metadata": map[string]any{"generateName": "test"},
}}, metav1.CreateOptions{})
require.NoError(t, err)
}
3 changes: 2 additions & 1 deletion bootstrap/crds_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package bootstrap

import (
"context"
"embed"

"k8s.io/client-go/rest"
Expand All @@ -10,6 +11,6 @@ import (
var crdFS embed.FS

func ExampleCRD() {
_ = CRD(&rest.Config{}, crdFS, "example")
_ = CRDs(context.Background(), &rest.Config{}, crdFS, "example")
// Output:
}
5 changes: 4 additions & 1 deletion bootstrap/example/crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
apiVersion: "apiextensions.k8s.io/v1"
kind: "CustomResourceDefinition"
metadata:
name: "mytype.example.com"
name: "mytypes.example.com"
spec:
group: "example.com"
names:
Expand All @@ -15,3 +15,6 @@ spec:
- name: "v1"
served: true
storage: true
schema:
openAPIV3Schema:
type: "object"
4 changes: 2 additions & 2 deletions bootstrap/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package bootstrap
import (
"bytes"
"context"
"encoding/json"
"errors"
"io"
"os"
Expand All @@ -29,7 +30,6 @@ import (
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/dynamic"
"k8s.io/utils/pointer"
"sigs.k8s.io/controller-runtime/pkg/client"
)

// KubeResourceObject is satisfied by any standard kube object.
Expand Down Expand Up @@ -78,7 +78,7 @@ func ResourceFromFile[O KubeResourceObject](ctx context.Context, fieldManager st
return hash, err
}

data, err := client.Apply.Data(objectDef)
data, err := json.Marshal(objectDef)
if err != nil {
return hash, err
}
Expand Down
Loading

0 comments on commit a8e0815

Please sign in to comment.