Skip to content

Commit ab7825e

Browse files
authored
Merge pull request #1486 from DirectXMan12/feature/envtest-secure-port
⚠️ Envtest refactor, support for 1.20+ API servers (secure serving)
2 parents 9cbdc4a + e77a2fc commit ab7825e

30 files changed

+2800
-459
lines changed

.golangci.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ linters:
2424
- deadcode
2525
- errcheck
2626
- varcheck
27-
- goconst
2827
- unparam
2928
- ineffassign
3029
- nakedret
3130
- gocyclo
32-
- lll
3331
- dupl
3432
- goimports
3533
- golint
34+
# disabled:
35+
# - goconst is overly aggressive
36+
# - lll generally just complains about flag help & error strings that are human-readable

examples/scratch-env/go.mod

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ go 1.15
44

55
require (
66
github.com/spf13/pflag v1.0.5
7-
k8s.io/client-go v0.19.2
87
sigs.k8s.io/controller-runtime v0.0.0-00010101000000-000000000000
98
)
109

examples/scratch-env/go.sum

+379-195
Large diffs are not rendered by default.

examples/scratch-env/main.go

+24-36
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,11 @@ package main
22

33
import (
44
goflag "flag"
5-
"fmt"
6-
"io"
75
"io/ioutil"
86
"os"
97

108
flag "github.com/spf13/pflag"
119

12-
"k8s.io/client-go/tools/clientcmd"
13-
kcapi "k8s.io/client-go/tools/clientcmd/api"
1410
ctrl "sigs.k8s.io/controller-runtime"
1511
"sigs.k8s.io/controller-runtime/pkg/envtest"
1612
"sigs.k8s.io/controller-runtime/pkg/log/zap"
@@ -22,25 +18,6 @@ var (
2218
attachControlPlaneOut = flag.Bool("debug-env", false, "attach to test env (apiserver & etcd) output -- just a convinience flag to force KUBEBUILDER_ATTACH_CONTROL_PLANE_OUTPUT=true")
2319
)
2420

25-
func writeKubeConfig(kubeConfig *kcapi.Config, kubeconfigFile *os.File) error {
26-
defer kubeconfigFile.Close()
27-
28-
contents, err := clientcmd.Write(*kubeConfig)
29-
if err != nil {
30-
return fmt.Errorf("unable to serialize kubeconfig file: %w", err)
31-
}
32-
33-
amt, err := kubeconfigFile.Write(contents)
34-
if err != nil {
35-
return fmt.Errorf("unable to write kubeconfig file: %w", err)
36-
}
37-
if amt != len(contents) {
38-
fmt.Errorf("unable to write all of the kubeconfig file: %w", io.ErrShortWrite)
39-
}
40-
41-
return nil
42-
}
43-
4421
// have a separate function so we can return an exit code w/o skipping defers
4522
func runMain() int {
4623
loggerOpts := &zap.Options{
@@ -68,34 +45,45 @@ func runMain() int {
6845
cfg, err := env.Start()
6946
if err != nil {
7047
log.Error(err, "unable to start the test environment")
48+
// shut down the environment in case we started it and failed while
49+
// installing CRDs or provisioning users.
50+
if err := env.Stop(); err != nil {
51+
log.Error(err, "unable to stop the test environment after an error (this might be expected, but just though you should know)")
52+
}
7153
return 1
7254
}
7355

7456
log.Info("apiserver running", "host", cfg.Host)
7557

58+
// NB(directxman12): this group is unfortunately named, but various
59+
// kubernetes versions require us to use it to get "admin" access.
60+
user, err := env.ControlPlane.AddUser(envtest.User{
61+
Name: "envtest-admin",
62+
Groups: []string{"system:masters"},
63+
}, nil)
64+
if err != nil {
65+
log.Error(err, "unable to provision admin user, continuing on without it")
66+
return 1
67+
}
68+
7669
// TODO(directxman12): add support for writing to a new context in an existing file
7770
kubeconfigFile, err := ioutil.TempFile("", "scratch-env-kubeconfig-")
7871
if err != nil {
7972
log.Error(err, "unable to create kubeconfig file, continuing on without it")
80-
} else {
81-
defer os.Remove(kubeconfigFile.Name())
73+
return 1
74+
}
75+
defer os.Remove(kubeconfigFile.Name())
8276

77+
{
8378
log := log.WithValues("path", kubeconfigFile.Name())
8479
log.V(1).Info("Writing kubeconfig")
8580

86-
// TODO(directxman12): this config isn't quite fully specified, but I
87-
// think it's the best we can do for now -- I don't see any obvious
88-
// "rest.Config --> clientcmdapi.Config" helper
89-
kubeConfig := kcapi.NewConfig()
90-
kubeConfig.Clusters["scratch-env"] = &kcapi.Cluster{
91-
Server: fmt.Sprintf("http://%s", cfg.Host),
81+
kubeConfig, err := user.KubeConfig()
82+
if err != nil {
83+
log.Error(err, "unable to create kubeconfig")
9284
}
93-
kcCtx := kcapi.NewContext()
94-
kcCtx.Cluster = "scratch-env"
95-
kubeConfig.Contexts["scratch-env"] = kcCtx
96-
kubeConfig.CurrentContext = "scratch-env"
9785

98-
if err := writeKubeConfig(kubeConfig, kubeconfigFile); err != nil {
86+
if _, err := kubeconfigFile.Write(kubeConfig); err != nil {
9987
log.Error(err, "unable to save kubeconfig")
10088
return 1
10189
}

pkg/cluster/cluster_suite_test.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,14 @@ var _ = BeforeSuite(func(done Done) {
5252
cfg, err = testenv.Start()
5353
Expect(err).NotTo(HaveOccurred())
5454

55-
clientTransport = &http.Transport{}
56-
cfg.Transport = clientTransport
55+
cfg.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
56+
// NB(directxman12): we can't set Transport *and* use TLS options,
57+
// so we grab the transport right after it gets created so that we can
58+
// type-assert on it (hopefully)?
59+
// hopefully this doesn't break 🤞
60+
clientTransport = rt.(*http.Transport)
61+
return rt
62+
}
5763

5864
clientset, err = kubernetes.NewForConfig(cfg)
5965
Expect(err).NotTo(HaveOccurred())

pkg/controller/controller_suite_test.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,14 @@ var _ = BeforeSuite(func(done Done) {
6868
cfg, err = testenv.Start()
6969
Expect(err).NotTo(HaveOccurred())
7070

71-
clientTransport = &http.Transport{}
72-
cfg.Transport = clientTransport
71+
cfg.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
72+
// NB(directxman12): we can't set Transport *and* use TLS options,
73+
// so we grab the transport right after it gets created so that we can
74+
// type-assert on it (hopefully)?
75+
// hopefully this doesn't break 🤞
76+
clientTransport = rt.(*http.Transport)
77+
return rt
78+
}
7379

7480
clientset, err = kubernetes.NewForConfig(cfg)
7581
Expect(err).NotTo(HaveOccurred())

pkg/envtest/crd.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func InstallCRDs(config *rest.Config, options CRDInstallOptions) ([]client.Objec
9595

9696
// Read the CRD yamls into options.CRDs
9797
if err := readCRDFiles(&options); err != nil {
98-
return nil, err
98+
return nil, fmt.Errorf("unable to read CRD files: %w", err)
9999
}
100100

101101
if err := modifyConversionWebhooks(options.CRDs, options.Scheme, options.WebhookOptions); err != nil {
@@ -104,12 +104,12 @@ func InstallCRDs(config *rest.Config, options CRDInstallOptions) ([]client.Objec
104104

105105
// Create the CRDs in the apiserver
106106
if err := CreateCRDs(config, options.CRDs); err != nil {
107-
return options.CRDs, err
107+
return options.CRDs, fmt.Errorf("unable to create CRD instances: %w", err)
108108
}
109109

110110
// Wait for the CRDs to appear as Resources in the apiserver
111111
if err := WaitForCRDs(config, options.CRDs, options); err != nil {
112-
return options.CRDs, err
112+
return options.CRDs, fmt.Errorf("something went wrong waiting for CRDs to appear as API resources: %w", err)
113113
}
114114

115115
return options.CRDs, nil
@@ -281,7 +281,7 @@ func UninstallCRDs(config *rest.Config, options CRDInstallOptions) error {
281281
func CreateCRDs(config *rest.Config, crds []client.Object) error {
282282
cs, err := client.New(config, client.Options{})
283283
if err != nil {
284-
return err
284+
return fmt.Errorf("unable to create client: %w", err)
285285
}
286286

287287
// Create each CRD
@@ -292,10 +292,10 @@ func CreateCRDs(config *rest.Config, crds []client.Object) error {
292292
switch {
293293
case apierrors.IsNotFound(err):
294294
if err := cs.Create(context.TODO(), crd); err != nil {
295-
return err
295+
return fmt.Errorf("unable to create CRD %q: %w", crd.GetName(), err)
296296
}
297297
case err != nil:
298-
return err
298+
return fmt.Errorf("unable to get CRD %q to check if it exists: %w", crd.GetName(), err)
299299
default:
300300
log.V(1).Info("CRD already exists, updating", "crd", crd.GetName())
301301
if err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {

0 commit comments

Comments
 (0)