From 7ff3b3d9cbbde7a245f3078c1ed6af6f67001ad7 Mon Sep 17 00:00:00 2001 From: Ryan Richard Date: Wed, 18 Jan 2023 14:39:22 -0800 Subject: [PATCH] Code changes to support Kube 0.26 deps --- cmd/pinniped-server/main.go | 9 ++- .../concierge/impersonator/impersonator.go | 8 +-- .../impersonator/impersonator_test.go | 33 ++++++--- .../controller/kubecertagent/kubecertagent.go | 8 +-- .../kubecertagent/kubecertagent_test.go | 14 ++-- .../mocks/mockpodcommandexecutor.go | 11 +-- .../kubecertagent/pod_command_executor.go | 10 +-- .../pod_command_executor_test.go | 5 +- internal/controllerlib/option.go | 8 ++- internal/kubeclient/kubeclient_test.go | 34 +-------- internal/psets/psets.go | 70 +++++++++++++++++++ internal/upstreamoidc/upstreamoidc.go | 4 +- test/integration/leaderelection_test.go | 8 +-- 13 files changed, 139 insertions(+), 83 deletions(-) create mode 100644 internal/psets/psets.go diff --git a/cmd/pinniped-server/main.go b/cmd/pinniped-server/main.go index b683b324..0d01ed60 100644 --- a/cmd/pinniped-server/main.go +++ b/cmd/pinniped-server/main.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // Package main is the combined entrypoint for all Pinniped server components. @@ -12,13 +12,12 @@ import ( "os" "path/filepath" - "k8s.io/apimachinery/pkg/util/sets" - - // this side effect import ensures that we use fipsonly crypto in fips_strict mode. concierge "go.pinniped.dev/internal/concierge/server" + // this side effect import ensures that we use fipsonly crypto in fips_strict mode. _ "go.pinniped.dev/internal/crypto/ptls" lua "go.pinniped.dev/internal/localuserauthenticator" "go.pinniped.dev/internal/plog" + "go.pinniped.dev/internal/psets" supervisor "go.pinniped.dev/internal/supervisor/server" ) @@ -38,7 +37,7 @@ func main() { } binary := filepath.Base(os.Args[0]) if subcommands[binary] == nil { - fail(fmt.Errorf("must be invoked as one of %v, not %q", sets.StringKeySet(subcommands).List(), binary)) + fail(fmt.Errorf("must be invoked as one of %v, not %q", psets.StringKeySet(subcommands).List(), binary)) } subcommands[binary]() } diff --git a/internal/concierge/impersonator/impersonator.go b/internal/concierge/impersonator/impersonator.go index 6a9783dd..e3779ad2 100644 --- a/internal/concierge/impersonator/impersonator.go +++ b/internal/concierge/impersonator/impersonator.go @@ -1,4 +1,4 @@ -// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package impersonator @@ -192,7 +192,7 @@ func newInternal( //nolint:funlen // yeah, it's kind of long. defer impersonationProxyCompleted.ServeHTTP(w, r) impersonationProxy.ServeHTTP(w, r) })) - handler = filterlatency.TrackStarted(handler, "impersonationproxy") + handler = filterlatency.TrackStarted(handler, c.TracerProvider, "impersonationproxy") // The standard Kube handler chain (authn, authz, impersonation, audit, etc). // See the genericapiserver.DefaultBuildHandlerChain func for details. @@ -201,12 +201,12 @@ func newInternal( //nolint:funlen // yeah, it's kind of long. // we need to grab the bearer token before WithAuthentication deletes it. handler = filterlatency.TrackCompleted(handler) handler = withBearerTokenPreservation(handler) - handler = filterlatency.TrackStarted(handler, "bearertokenpreservation") + handler = filterlatency.TrackStarted(handler, c.TracerProvider, "bearertokenpreservation") // Always set security headers so browsers do the right thing. handler = filterlatency.TrackCompleted(handler) handler = securityheader.Wrap(handler) - handler = filterlatency.TrackStarted(handler, "securityheaders") + handler = filterlatency.TrackStarted(handler, c.TracerProvider, "securityheaders") return handler } diff --git a/internal/concierge/impersonator/impersonator_test.go b/internal/concierge/impersonator/impersonator_test.go index cf68a510..1a102143 100644 --- a/internal/concierge/impersonator/impersonator_test.go +++ b/internal/concierge/impersonator/impersonator_test.go @@ -1,4 +1,4 @@ -// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package impersonator @@ -58,6 +58,11 @@ import ( ) func TestImpersonator(t *testing.T) { + const ( + priorityLevelConfigurationsVersion = "v1beta3" + flowSchemasVersion = "v1beta3" + ) + ca, err := certauthority.New("ca", time.Hour) require.NoError(t, err) caKey, err := ca.PrivateKeyToPEM() @@ -714,8 +719,8 @@ func TestImpersonator(t *testing.T) { secure := ptls.Secure(rootCAs) switch r.URL.Path { case "/api/v1/namespaces/kube-system/configmaps", - "/apis/flowcontrol.apiserver.k8s.io/v1beta2/prioritylevelconfigurations", - "/apis/flowcontrol.apiserver.k8s.io/v1beta2/flowschemas", + fmt.Sprintf("/apis/flowcontrol.apiserver.k8s.io/%s/prioritylevelconfigurations", priorityLevelConfigurationsVersion), + fmt.Sprintf("/apis/flowcontrol.apiserver.k8s.io/%s/flowschemas", flowSchemasVersion), "/healthz": default: if !httpstream.IsUpgradeRequest(r) { @@ -736,8 +741,8 @@ func TestImpersonator(t *testing.T) { http.NotFound(w, r) return - case "/apis/flowcontrol.apiserver.k8s.io/v1beta2/prioritylevelconfigurations", - "/apis/flowcontrol.apiserver.k8s.io/v1beta2/flowschemas": + case fmt.Sprintf("/apis/flowcontrol.apiserver.k8s.io/%s/prioritylevelconfigurations", priorityLevelConfigurationsVersion), + fmt.Sprintf("/apis/flowcontrol.apiserver.k8s.io/%s/flowschemas", flowSchemasVersion): // ignore requests related to priority and fairness logic require.Equal(t, http.MethodGet, r.Method) http.NotFound(w, r) @@ -1051,7 +1056,11 @@ func TestImpersonator(t *testing.T) { } func TestImpersonatorHTTPHandler(t *testing.T) { - const testUser = "test-user" + const ( + testUser = "test-user" + priorityLevelConfigurationsVersion = "v1beta3" + flowSchemasVersion = "v1beta3" + ) testGroups := []string{"test-group-1", "test-group-2"} testExtra := map[string][]string{ @@ -1149,7 +1158,9 @@ func TestImpersonatorHTTPHandler(t *testing.T) { Groups: testGroups, Extra: testExtra, }, nil, "") - ctx := audit.WithAuditContext(req.Context(), nil) + ctx := audit.WithAuditContext(req.Context()) + ac := audit.AuditContextFrom(ctx) + ac.Event = nil req = req.WithContext(ctx) return req }(), @@ -1814,8 +1825,8 @@ func TestImpersonatorHTTPHandler(t *testing.T) { secure := ptls.Secure(rootCAs) switch r.URL.Path { case "/api/v1/namespaces/kube-system/configmaps", - "/apis/flowcontrol.apiserver.k8s.io/v1beta2/prioritylevelconfigurations", - "/apis/flowcontrol.apiserver.k8s.io/v1beta2/flowschemas", + fmt.Sprintf("/apis/flowcontrol.apiserver.k8s.io/%s/prioritylevelconfigurations", priorityLevelConfigurationsVersion), + fmt.Sprintf("/apis/flowcontrol.apiserver.k8s.io/%s/flowschemas", flowSchemasVersion), "/healthz": default: if !httpstream.IsUpgradeRequest(r) { @@ -1925,7 +1936,9 @@ func newRequest(t *testing.T, h http.Header, userInfo user.Info, event *auditint if event != nil { ae = event } - ctx = audit.WithAuditContext(ctx, &audit.AuditContext{Event: ae}) + ctx = audit.WithAuditContext(ctx) + ac := audit.AuditContextFrom(ctx) + ac.Event = ae reqInfo := &request.RequestInfo{ IsResourceRequest: false, diff --git a/internal/controller/kubecertagent/kubecertagent.go b/internal/controller/kubecertagent/kubecertagent.go index e6b6441b..d3bf9a7e 100644 --- a/internal/controller/kubecertagent/kubecertagent.go +++ b/internal/controller/kubecertagent/kubecertagent.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 // Package kubecertagent provides controllers that ensure a pod (the kube-cert-agent), is @@ -317,7 +317,7 @@ func (c *agentController) Sync(ctx controllerlib.Context) error { } // Load the certificate and key from the agent pod into our in-memory signer. - if err := c.loadSigningKey(newestAgentPod); err != nil { + if err := c.loadSigningKey(ctx.Context, newestAgentPod); err != nil { return c.failStrategyAndErr(ctx.Context, credIssuer, firstErr(depErr, err), configv1alpha1.CouldNotFetchKeyStrategyReason) } @@ -341,14 +341,14 @@ func (c *agentController) Sync(ctx controllerlib.Context) error { }) } -func (c *agentController) loadSigningKey(agentPod *corev1.Pod) error { +func (c *agentController) loadSigningKey(ctx context.Context, agentPod *corev1.Pod) error { // If we remember successfully loading the key from this pod recently, we can skip this step and return immediately. if _, exists := c.execCache.Get(agentPod.UID); exists { return nil } // Exec into the agent pod and cat out the certificate and the key. - outputJSON, err := c.executor.Exec(agentPod.Namespace, agentPod.Name, "pinniped-concierge-kube-cert-agent", "print") + outputJSON, err := c.executor.Exec(ctx, agentPod.Namespace, agentPod.Name, "pinniped-concierge-kube-cert-agent", "print") if err != nil { return fmt.Errorf("could not exec into agent pod %s/%s: %w", agentPod.Namespace, agentPod.Name, err) } diff --git a/internal/controller/kubecertagent/kubecertagent_test.go b/internal/controller/kubecertagent/kubecertagent_test.go index 48634539..a2aa4971 100644 --- a/internal/controller/kubecertagent/kubecertagent_test.go +++ b/internal/controller/kubecertagent/kubecertagent_test.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package kubecertagent @@ -229,7 +229,7 @@ func TestAgentController(t *testing.T) { } mockExecSucceeds := func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) { - executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). + executor.Exec(gomock.Any(), "concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). Return(`{"tls.crt": "dGVzdC1jZXJ0", "tls.key": "dGVzdC1rZXk="}`, nil) // "test-cert" / "test-key" dynamicCert.SetCertKeyContent([]byte("test-cert"), []byte("test-key")). Return(nil) @@ -740,7 +740,7 @@ func TestAgentController(t *testing.T) { validClusterInfoConfigMap, }, mocks: func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) { - executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). + executor.Exec(gomock.Any(), "concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). Return("", fmt.Errorf("some exec error")). AnyTimes() }, @@ -769,7 +769,7 @@ func TestAgentController(t *testing.T) { validClusterInfoConfigMap, }, mocks: func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) { - executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). + executor.Exec(gomock.Any(), "concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). Return("bogus-data", nil). AnyTimes() }, @@ -798,7 +798,7 @@ func TestAgentController(t *testing.T) { validClusterInfoConfigMap, }, mocks: func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) { - executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). + executor.Exec(gomock.Any(), "concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). Return(`{"tls.crt": "invalid"}`, nil). AnyTimes() }, @@ -827,7 +827,7 @@ func TestAgentController(t *testing.T) { validClusterInfoConfigMap, }, mocks: func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) { - executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). + executor.Exec(gomock.Any(), "concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). Return(`{"tls.crt": "dGVzdAo=", "tls.key": "invalid"}`, nil). AnyTimes() }, @@ -856,7 +856,7 @@ func TestAgentController(t *testing.T) { validClusterInfoConfigMap, }, mocks: func(t *testing.T, executor *mocks.MockPodCommandExecutorMockRecorder, dynamicCert *mocks.MockDynamicCertPrivateMockRecorder, execCache *cache.Expiring) { - executor.Exec("concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). + executor.Exec(gomock.Any(), "concierge", "pinniped-concierge-kube-cert-agent-xyz-1234", "pinniped-concierge-kube-cert-agent", "print"). Return(`{"tls.crt": "dGVzdC1jZXJ0", "tls.key": "dGVzdC1rZXk="}`, nil). // "test-cert" / "test-key" AnyTimes() dynamicCert.SetCertKeyContent([]byte("test-cert"), []byte("test-key")). diff --git a/internal/controller/kubecertagent/mocks/mockpodcommandexecutor.go b/internal/controller/kubecertagent/mocks/mockpodcommandexecutor.go index bb87467e..781cb9fe 100644 --- a/internal/controller/kubecertagent/mocks/mockpodcommandexecutor.go +++ b/internal/controller/kubecertagent/mocks/mockpodcommandexecutor.go @@ -9,6 +9,7 @@ package mocks import ( + context "context" reflect "reflect" gomock "github.com/golang/mock/gomock" @@ -38,10 +39,10 @@ func (m *MockPodCommandExecutor) EXPECT() *MockPodCommandExecutorMockRecorder { } // Exec mocks base method. -func (m *MockPodCommandExecutor) Exec(arg0, arg1 string, arg2 ...string) (string, error) { +func (m *MockPodCommandExecutor) Exec(arg0 context.Context, arg1, arg2 string, arg3 ...string) (string, error) { m.ctrl.T.Helper() - varargs := []interface{}{arg0, arg1} - for _, a := range arg2 { + varargs := []interface{}{arg0, arg1, arg2} + for _, a := range arg3 { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Exec", varargs...) @@ -51,8 +52,8 @@ func (m *MockPodCommandExecutor) Exec(arg0, arg1 string, arg2 ...string) (string } // Exec indicates an expected call of Exec. -func (mr *MockPodCommandExecutorMockRecorder) Exec(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call { +func (mr *MockPodCommandExecutorMockRecorder) Exec(arg0, arg1, arg2 interface{}, arg3 ...interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - varargs := append([]interface{}{arg0, arg1}, arg2...) + varargs := append([]interface{}{arg0, arg1, arg2}, arg3...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockPodCommandExecutor)(nil).Exec), varargs...) } diff --git a/internal/controller/kubecertagent/pod_command_executor.go b/internal/controller/kubecertagent/pod_command_executor.go index 242f6435..234a4e7a 100644 --- a/internal/controller/kubecertagent/pod_command_executor.go +++ b/internal/controller/kubecertagent/pod_command_executor.go @@ -1,10 +1,11 @@ -// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package kubecertagent import ( "bytes" + "context" v1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" @@ -15,7 +16,7 @@ import ( // PodCommandExecutor can exec a command in a pod located via namespace and name. type PodCommandExecutor interface { - Exec(podNamespace string, podName string, commandAndArgs ...string) (stdoutResult string, err error) + Exec(ctx context.Context, podNamespace string, podName string, commandAndArgs ...string) (stdoutResult string, err error) } type kubeClientPodCommandExecutor struct { @@ -31,8 +32,7 @@ func NewPodCommandExecutor(kubeConfig *restclient.Config, kubeClient kubernetes. return &kubeClientPodCommandExecutor{kubeConfig: kubeConfig, kubeClient: kubeClient} } -func (s *kubeClientPodCommandExecutor) Exec(podNamespace string, podName string, commandAndArgs ...string) (string, error) { - // TODO: see if we can add a timeout or make this cancelable somehow +func (s *kubeClientPodCommandExecutor) Exec(ctx context.Context, podNamespace string, podName string, commandAndArgs ...string) (string, error) { request := s.kubeClient. CoreV1(). RESTClient(). @@ -55,7 +55,7 @@ func (s *kubeClientPodCommandExecutor) Exec(podNamespace string, podName string, } var stdoutBuf bytes.Buffer - if err := executor.Stream(remotecommand.StreamOptions{Stdout: &stdoutBuf}); err != nil { + if err := executor.StreamWithContext(ctx, remotecommand.StreamOptions{Stdout: &stdoutBuf}); err != nil { return "", err } return stdoutBuf.String(), nil diff --git a/internal/controller/kubecertagent/pod_command_executor_test.go b/internal/controller/kubecertagent/pod_command_executor_test.go index 934f102d..184382fc 100644 --- a/internal/controller/kubecertagent/pod_command_executor_test.go +++ b/internal/controller/kubecertagent/pod_command_executor_test.go @@ -1,9 +1,10 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package kubecertagent import ( + "context" "net/http" "testing" @@ -36,7 +37,7 @@ func TestSecureTLS(t *testing.T) { // build this exactly like our production could does podCommandExecutor := NewPodCommandExecutor(client.JSONConfig, client.Kubernetes) - got, err := podCommandExecutor.Exec("podNamespace", "podName", "command", "arg1", "arg2") + got, err := podCommandExecutor.Exec(context.Background(), "podNamespace", "podName", "command", "arg1", "arg2") require.Equal(t, &errors.StatusError{}, err) require.Empty(t, got) diff --git a/internal/controllerlib/option.go b/internal/controllerlib/option.go index 9b3b4510..894a1366 100644 --- a/internal/controllerlib/option.go +++ b/internal/controllerlib/option.go @@ -1,4 +1,4 @@ -// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package controllerlib @@ -59,7 +59,7 @@ func WithInformer(getter InformerGetter, filter Filter, opt InformerOption) Opti return } - informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + _, err := informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { object := metaOrDie(obj) if filter.Add(object) { @@ -113,6 +113,10 @@ func WithInformer(getter InformerGetter, filter Filter, opt InformerOption) Opti } }, }) + if err != nil { + // Shouldn't really happen. + panic(die(fmt.Sprintf("got error from AddEventHandler: %s", err.Error()))) + } }) } diff --git a/internal/kubeclient/kubeclient_test.go b/internal/kubeclient/kubeclient_test.go index 57760d30..6c8269cf 100644 --- a/internal/kubeclient/kubeclient_test.go +++ b/internal/kubeclient/kubeclient_test.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package kubeclient @@ -974,21 +974,6 @@ func TestUnwrap(t *testing.T) { testUnwrap(t, execClient, serverSubjects) }) - t.Run("gcp client", func(t *testing.T) { - t.Parallel() // make sure to run in parallel to confirm that our client-go TLS cache busting works (i.e. assert no data races) - - gcpClient := makeClient(t, restConfig, func(config *rest.Config) { - config.AuthProvider = &clientcmdapi.AuthProviderConfig{ - Name: "gcp", - Config: map[string]string{ - "cmd-path": `echo {"access_token":"fake","token_expiry":"2200-01-02T15:04:05.999999999Z07:00"}`, - }, - } - }) - - testUnwrap(t, gcpClient, serverSubjects) - }) - t.Run("oidc client", func(t *testing.T) { t.Parallel() // make sure to run in parallel to confirm that our client-go TLS cache busting works (i.e. assert no data races) @@ -1004,23 +989,6 @@ func TestUnwrap(t *testing.T) { testUnwrap(t, oidcClient, serverSubjects) }) - - t.Run("azure client", func(t *testing.T) { - t.Parallel() // make sure to run in parallel to confirm that our client-go TLS cache busting works (i.e. assert no data races) - - azureClient := makeClient(t, restConfig, func(config *rest.Config) { - config.AuthProvider = &clientcmdapi.AuthProviderConfig{ - Name: "azure", - Config: map[string]string{ - "client-id": "pinny", - "tenant-id": "danger", - "apiserver-id": "1234", - }, - } - }) - - testUnwrap(t, azureClient, serverSubjects) - }) } func testUnwrap(t *testing.T, client *Client, serverSubjects [][]byte) { diff --git a/internal/psets/psets.go b/internal/psets/psets.go new file mode 100644 index 00000000..56730c55 --- /dev/null +++ b/internal/psets/psets.go @@ -0,0 +1,70 @@ +// Copyright 2023 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package psets + +import ( + "reflect" + "sort" +) + +// These were copied from https://github.com/kubernetes/kubernetes/tree/v1.25.5/staging/src/k8s.io/apimachinery/pkg/util/sets +// which is the last version before they were converted to generic functions which require the use +// of Go 1.18+ to compile. This is not a full copy of the files from k/k, but rather only copies of the +// functions that we actually use. When we are ready to require the use of Go 1.18+ to compile Pinniped, +// then we can go back to using the version of this package from the k8s libraries. Our use +// of this package was very minimal, so its easy enough to just copy the few functions that we were +// actually using to keep Go 1.17 compatibility a little longer. + +// Empty is public since it is used by some internal API objects for conversions between external +// string arrays and internal sets, and conversion logic requires public types today. +type Empty struct{} + +// sets.String is a set of strings, implemented via map[string]struct{} for minimal memory consumption. +type String map[string]Empty + +// StringKeySet creates a String from a keys of a map[string](? extends interface{}). +// If the value passed in is not actually a map, this will panic. +func StringKeySet(theMap interface{}) String { + v := reflect.ValueOf(theMap) + ret := String{} + + for _, keyValue := range v.MapKeys() { + ret.Insert(keyValue.Interface().(string)) + } + return ret +} + +// Insert adds items to the set. +func (s String) Insert(items ...string) String { + for _, item := range items { + s[item] = Empty{} + } + return s +} + +// Has returns true if and only if item is contained in the set. +func (s String) Has(item string) bool { + _, contained := s[item] + return contained +} + +type sortableSliceOfString []string + +func (s sortableSliceOfString) Len() int { return len(s) } +func (s sortableSliceOfString) Less(i, j int) bool { return lessString(s[i], s[j]) } +func (s sortableSliceOfString) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// List returns the contents as a sorted string slice. +func (s String) List() []string { + res := make(sortableSliceOfString, 0, len(s)) + for key := range s { + res = append(res, key) + } + sort.Sort(res) + return []string(res) +} + +func lessString(lhs, rhs string) bool { + return lhs < rhs +} diff --git a/internal/upstreamoidc/upstreamoidc.go b/internal/upstreamoidc/upstreamoidc.go index ff8b83fc..9a313c40 100644 --- a/internal/upstreamoidc/upstreamoidc.go +++ b/internal/upstreamoidc/upstreamoidc.go @@ -18,12 +18,12 @@ import ( "golang.org/x/oauth2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/sets" oidcapi "go.pinniped.dev/generated/latest/apis/supervisor/oidc" "go.pinniped.dev/internal/httputil/httperr" "go.pinniped.dev/internal/oidc/provider" "go.pinniped.dev/internal/plog" + "go.pinniped.dev/internal/psets" "go.pinniped.dev/pkg/oidcclient/nonce" "go.pinniped.dev/pkg/oidcclient/oidctypes" "go.pinniped.dev/pkg/oidcclient/pkce" @@ -421,7 +421,7 @@ func maybeLogClaims(msg, name string, claims map[string]interface{}) { } if plog.Enabled(plog.LevelDebug) { // log keys at debug level - keys := sets.StringKeySet(claims).List() // note: this is only safe because the compiler asserts that claims is a map[string] + keys := psets.StringKeySet(claims).List() // note: this is only safe because the compiler asserts that claims is a map[string] plog.Debug(msg, "providerName", name, "keys", keys) return } diff --git a/test/integration/leaderelection_test.go b/test/integration/leaderelection_test.go index d9da6c57..bcfe9698 100644 --- a/test/integration/leaderelection_test.go +++ b/test/integration/leaderelection_test.go @@ -1,4 +1,4 @@ -// Copyright 2021 the Pinniped contributors. All Rights Reserved. +// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package integration @@ -17,13 +17,13 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/rand" - "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/util/retry" "k8s.io/utils/pointer" "go.pinniped.dev/internal/downward" "go.pinniped.dev/internal/kubeclient" "go.pinniped.dev/internal/leaderelection" + "go.pinniped.dev/internal/psets" "go.pinniped.dev/test/testlib" ) @@ -175,7 +175,7 @@ func leaderElectionClients(t *testing.T, namespace *corev1.Namespace, leaseName clients[identity], cancels[identity] = leaderElectionClient(t, namespace, leaseName, identity) } - t.Logf("running leader election client tests with %d clients: %v", len(clients), sets.StringKeySet(clients).List()) + t.Logf("running leader election client tests with %d clients: %v", len(clients), psets.StringKeySet(clients).List()) return clients, cancels } @@ -191,7 +191,7 @@ func pickRandomLeaderElectionClient(clients map[string]*kubeclient.Client) *kube func waitForIdentity(ctx context.Context, t *testing.T, namespace *corev1.Namespace, leaseName string, clients map[string]*kubeclient.Client) *coordinationv1.Lease { t.Helper() - identities := sets.StringKeySet(clients) + identities := psets.StringKeySet(clients) var out *coordinationv1.Lease testlib.RequireEventuallyWithoutError(t, func() (bool, error) {