Skip to content
This repository has been archived by the owner on Aug 12, 2024. It is now read-only.

Commit

Permalink
BundleDeployment: add and implement required serviceAccountName and i…
Browse files Browse the repository at this point in the history
…nstallNamespace fields
  • Loading branch information
joelanford committed Apr 10, 2024
1 parent 3d1ba69 commit d196f7b
Show file tree
Hide file tree
Showing 12 changed files with 246 additions and 944 deletions.
13 changes: 13 additions & 0 deletions api/v1alpha2/bundledeployment_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ const (

// BundleDeploymentSpec defines the desired state of BundleDeployment
type BundleDeploymentSpec struct {
//+kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
//+kubebuilder:validation:MaxLength:=63
// The namespace where the bundle should be installed. However, note that
// the bundle may contain resources that are cluster-scoped or that are
// installed in a different namespace. This namespace is expected to exist.
InstallNamespace string `json:"installNamespace"`

//+kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9.]*[a-z0-9])?$
//+kubebuilder:validation:MaxLength:=253
// ServiceAccountName is the name of the ServiceAccount to use to manage the resources in the bundle.
// The service account is expected to exist in the InstallNamespace.
ServiceAccountName string `json:"serviceAccountName"`

//+kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
// ProvisionerClassName sets the name of the provisioner that should reconcile this BundleDeployment.
ProvisionerClassName string `json:"provisionerClassName"`
Expand Down
40 changes: 38 additions & 2 deletions cmd/core/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package main

import (
"context"
"crypto/x509"
"flag"
"fmt"
Expand All @@ -31,8 +32,11 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
"k8s.io/kube-aggregator/pkg/apis/apiregistration"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/cache"
Expand All @@ -45,6 +49,7 @@ import (
helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client"

rukpakv1alpha2 "github.com/operator-framework/rukpak/api/v1alpha2"
"github.com/operator-framework/rukpak/internal/authentication"
"github.com/operator-framework/rukpak/internal/controllers/bundledeployment"
"github.com/operator-framework/rukpak/internal/finalizer"
"github.com/operator-framework/rukpak/internal/provisioner/plain"
Expand Down Expand Up @@ -202,18 +207,49 @@ func main() {
os.Exit(1)
}

cfgGetter, err := helmclient.NewActionConfigGetter(mgr.GetConfig(), mgr.GetRESTMapper(), mgr.GetLogger())
saGetter, err := corev1client.NewForConfig(cfg)
if err != nil {
setupLog.Error(err, "unable to create service account client")
os.Exit(1)
}

tg := authentication.NewTokenGetter(saGetter, 3600)
nsMapper := func(obj client.Object) (string, error) {
bd, ok := obj.(*rukpakv1alpha2.BundleDeployment)
if !ok {
return "", fmt.Errorf("cannot derive namespace from object of type %T", obj)
}
return bd.Spec.InstallNamespace, nil
}
rcm := func(ctx context.Context, obj client.Object, baseRestConfig *rest.Config) (*rest.Config, error) {
cfg := rest.AnonymousClientConfig(rest.CopyConfig(baseRestConfig))
bd, ok := obj.(*rukpakv1alpha2.BundleDeployment)
if !ok {
return cfg, nil
}
token, err := tg.Get(ctx, types.NamespacedName{Namespace: bd.Spec.InstallNamespace, Name: bd.Spec.ServiceAccountName})
if err != nil {
return nil, err
}
cfg.BearerToken = token
return cfg, nil
}
cfgGetter, err := helmclient.NewActionConfigGetter(mgr.GetConfig(), mgr.GetRESTMapper(),
helmclient.ClientNamespaceMapper(nsMapper),
helmclient.StorageNamespaceMapper(nsMapper),
helmclient.RestConfigMapper(rcm),
)
if err != nil {
setupLog.Error(err, "unable to create action config getter")
os.Exit(1)
}

acg, err := helmclient.NewActionClientGetter(cfgGetter)
if err != nil {
setupLog.Error(err, "unable to create action client getter")
os.Exit(1)
}
commonBDProvisionerOptions := []bundledeployment.Option{
bundledeployment.WithReleaseNamespace(systemNamespace),
bundledeployment.WithActionClientGetter(acg),
bundledeployment.WithFinalizers(bundleFinalizers),
bundledeployment.WithStorage(bundleStorage),
Expand Down
37 changes: 34 additions & 3 deletions cmd/helm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package main

import (
"context"
"crypto/x509"
"flag"
"fmt"
Expand All @@ -28,8 +29,11 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand All @@ -41,6 +45,7 @@ import (
helmclient "github.com/operator-framework/helm-operator-plugins/pkg/client"

rukpakv1alpha2 "github.com/operator-framework/rukpak/api/v1alpha2"
"github.com/operator-framework/rukpak/internal/authentication"
"github.com/operator-framework/rukpak/internal/controllers/bundledeployment"
"github.com/operator-framework/rukpak/internal/finalizer"
"github.com/operator-framework/rukpak/internal/provisioner/helm"
Expand Down Expand Up @@ -194,18 +199,44 @@ func main() {
os.Exit(1)
}

cfgGetter, err := helmclient.NewActionConfigGetter(mgr.GetConfig(), mgr.GetRESTMapper(), mgr.GetLogger())
saGetter, err := corev1client.NewForConfig(cfg)
if err != nil {
setupLog.Error(err, "unable to create action config getter")
setupLog.Error(err, "unable to create service account client")
os.Exit(1)
}

tg := authentication.NewTokenGetter(saGetter, 3600)
nsMapper := func(obj client.Object) (string, error) {
bd, ok := obj.(*rukpakv1alpha2.BundleDeployment)
if !ok {
return "", fmt.Errorf("cannot derive namespace from object of type %T", obj)
}
return bd.Spec.InstallNamespace, nil
}
rcm := func(ctx context.Context, obj client.Object, baseRestConfig *rest.Config) (*rest.Config, error) {
cfg := rest.AnonymousClientConfig(rest.CopyConfig(baseRestConfig))
bd, ok := obj.(*rukpakv1alpha2.BundleDeployment)
if !ok {
return cfg, nil
}
token, err := tg.Get(ctx, types.NamespacedName{Namespace: bd.Spec.InstallNamespace, Name: bd.Spec.ServiceAccountName})
if err != nil {
return nil, err
}
cfg.BearerToken = token
return cfg, nil
}
cfgGetter, err := helmclient.NewActionConfigGetter(mgr.GetConfig(), mgr.GetRESTMapper(),

Check failure on line 229 in cmd/helm/main.go

View workflow job for this annotation

GitHub Actions / lint

ineffectual assignment to err (ineffassign)
helmclient.ClientNamespaceMapper(nsMapper),
helmclient.StorageNamespaceMapper(nsMapper),
helmclient.RestConfigMapper(rcm),
)
acg, err := helmclient.NewActionClientGetter(cfgGetter)
if err != nil {
setupLog.Error(err, "unable to create action client getter")
os.Exit(1)
}
commonBDProvisionerOptions := []bundledeployment.Option{
bundledeployment.WithReleaseNamespace(systemNamespace),
bundledeployment.WithFinalizers(bundleFinalizers),
bundledeployment.WithActionClientGetter(acg),
bundledeployment.WithStorage(bundleStorage),
Expand Down
10 changes: 6 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,8 @@ require (
k8s.io/cli-runtime v0.29.2
k8s.io/client-go v0.29.3
k8s.io/component-base v0.29.3
k8s.io/kube-aggregator v0.29.2
k8s.io/kube-aggregator v0.29.3
k8s.io/utils v0.0.0-20240102154912-e7106e64919e
sigs.k8s.io/cli-utils v0.35.0
sigs.k8s.io/controller-runtime v0.17.2
sigs.k8s.io/yaml v1.4.0
)
Expand Down Expand Up @@ -155,9 +154,9 @@ require (
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.18.0 // indirect
github.com/prometheus/client_golang v1.19.0 // indirect
github.com/prometheus/client_model v0.6.0 // indirect
github.com/prometheus/common v0.47.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rubenv/sql-migrate v1.5.2 // indirect
Expand Down Expand Up @@ -215,8 +214,11 @@ require (
k8s.io/kubectl v0.29.2 // indirect
oras.land/oras-go v1.2.5 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 // indirect
sigs.k8s.io/cli-utils v0.35.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
)

replace github.com/operator-framework/helm-operator-plugins => github.com/joelanford/helm-operator v0.0.8-0.20240410201710-28cf4b214152
16 changes: 8 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/joelanford/helm-operator v0.0.8-0.20240410201710-28cf4b214152 h1:mqV/H66Qe/G+wtYMa35XP3loKkV0r3nkaalh+AeDlyQ=
github.com/joelanford/helm-operator v0.0.8-0.20240410201710-28cf4b214152/go.mod h1:d8jdQ8Yn4W0SIpEyIKljFtkGSRusC764rO36jwg8noI=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
Expand Down Expand Up @@ -390,8 +392,6 @@ github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bl
github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/operator-framework/api v0.23.0 h1:kHymOwcHBpBVujT49SKOCd4EVG7Odwj4wl3NbOR2LLA=
github.com/operator-framework/api v0.23.0/go.mod h1:oKcFOz+Xc1UhMi2Pzcp6qsO7wjS4r+yP7EQprQBXrfM=
github.com/operator-framework/helm-operator-plugins v0.1.3 h1:nwl9K1Pq0NZmanpEF/DYO00S7QO/iAmEdRIuLROrYpk=
github.com/operator-framework/helm-operator-plugins v0.1.3/go.mod h1:f/AR6r2DiSRK5zv9MD+NgWbayP6qDbQMw+unFuw0rPQ=
github.com/operator-framework/operator-lib v0.12.0 h1:OzpMU5N7mvFgg/uje8FUUeD24Ahq64R6TdN25uswCYA=
github.com/operator-framework/operator-lib v0.12.0/go.mod h1:ClpLUI7hctEF7F5DBe/kg041dq/4NLR7XC5tArY7bG4=
github.com/operator-framework/operator-registry v1.39.0 h1:GiAlmA2h16sLpLjVIuURd2ANm7wYoUbssGCJbdGauYw=
Expand All @@ -416,17 +416,17 @@ github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjz
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/common v0.47.0 h1:p5Cz0FNHo7SnWOmWmoRozVcjEp0bIVU8cV7OShpjL1k=
github.com/prometheus/common v0.47.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
Expand Down Expand Up @@ -753,8 +753,8 @@ k8s.io/component-base v0.29.3 h1:Oq9/nddUxlnrCuuR2K/jp6aflVvc0uDvxMzAWxnGzAo=
k8s.io/component-base v0.29.3/go.mod h1:Yuj33XXjuOk2BAaHsIGHhCKZQAgYKhqIxIjIr2UXYio=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-aggregator v0.29.2 h1:z9qJn5wlGmGaX6EfM7OEhr6fq6SBjDKR6tPRZ/qgxeY=
k8s.io/kube-aggregator v0.29.2/go.mod h1:QEuwzmMJJsg0eg1Gv+u4cWcYeJG2+8vN8/nTXBzopUo=
k8s.io/kube-aggregator v0.29.3 h1:5KvTyFN8sQq2imq8tMAHWEKoE64Zg9WSMaGX78KV6ps=
k8s.io/kube-aggregator v0.29.3/go.mod h1:xGJqV/SJJ1fbwTGfQLAZfwgqX1EMoaqfotDTkDrqqSk=
k8s.io/kube-openapi v0.0.0-20240221221325-2ac9dc51f3f1 h1:rtdnaWfP40MTKv7izH81gkWpZB45pZrwIxyZdPSn1mI=
k8s.io/kube-openapi v0.0.0-20240221221325-2ac9dc51f3f1/go.mod h1:Pa1PvrP7ACSkuX6I7KYomY6cmMA0Tx86waBhDUgoKPw=
k8s.io/kubectl v0.29.2 h1:uaDYaBhumvkwz0S2XHt36fK0v5IdNgL7HyUniwb2IUo=
Expand Down
97 changes: 97 additions & 0 deletions internal/authentication/tokengetter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package authentication

import (
"context"
"sync"
"time"

authenticationv1 "k8s.io/api/authentication/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/utils/ptr"
)

type TokenGetter struct {
client corev1client.ServiceAccountsGetter
expirationSeconds int64
tokens map[types.NamespacedName]*authenticationv1.TokenRequestStatus
tokenLocks keyLock[types.NamespacedName]
mu sync.RWMutex
}

func NewTokenGetter(client corev1client.ServiceAccountsGetter, expirationSeconds int64) *TokenGetter {
return &TokenGetter{
client: client,
expirationSeconds: expirationSeconds,
tokenLocks: newKeyLock[types.NamespacedName](),
tokens: map[types.NamespacedName]*authenticationv1.TokenRequestStatus{},
}
}

type keyLock[K comparable] struct {
locks map[K]*sync.Mutex
mu sync.Mutex
}

func newKeyLock[K comparable]() keyLock[K] {
return keyLock[K]{locks: map[K]*sync.Mutex{}}
}

func (k keyLock[K]) Lock(key K) {

Check failure on line 41 in internal/authentication/tokengetter.go

View workflow job for this annotation

GitHub Actions / lint

copylocks: Lock passes lock by value: github.com/operator-framework/rukpak/internal/authentication.keyLock[K] contains sync.Mutex (govet)
k.getLock(key).Lock()
}

func (k keyLock[K]) Unlock(key K) {

Check failure on line 45 in internal/authentication/tokengetter.go

View workflow job for this annotation

GitHub Actions / lint

copylocks: Unlock passes lock by value: github.com/operator-framework/rukpak/internal/authentication.keyLock[K] contains sync.Mutex (govet)
k.getLock(key).Unlock()
}

func (k keyLock[K]) getLock(key K) *sync.Mutex {

Check failure on line 49 in internal/authentication/tokengetter.go

View workflow job for this annotation

GitHub Actions / lint

copylocks: getLock passes lock by value: github.com/operator-framework/rukpak/internal/authentication.keyLock[K] contains sync.Mutex (govet)
k.mu.Lock()
defer k.mu.Unlock()

lock, ok := k.locks[key]
if !ok {
lock = &sync.Mutex{}
k.locks[key] = lock
}
return lock
}

func (t *TokenGetter) Get(ctx context.Context, key types.NamespacedName) (string, error) {
t.tokenLocks.Lock(key)
defer t.tokenLocks.Unlock(key)

t.mu.RLock()
token, ok := t.tokens[key]
t.mu.RUnlock()

expireTime := time.Time{}
if ok {
expireTime = token.ExpirationTimestamp.Time
}

fiveMinutesAfterNow := metav1.Now().Add(5 * time.Minute)
if expireTime.Before(fiveMinutesAfterNow) {
var err error
token, err = t.getToken(ctx, key)
if err != nil {
return "", err
}
t.mu.Lock()
t.tokens[key] = token
t.mu.Unlock()
}

return token.Token, nil
}

func (t *TokenGetter) getToken(ctx context.Context, key types.NamespacedName) (*authenticationv1.TokenRequestStatus, error) {
req, err := t.client.ServiceAccounts(key.Namespace).CreateToken(ctx, key.Name, &authenticationv1.TokenRequest{Spec: authenticationv1.TokenRequestSpec{
ExpirationSeconds: ptr.To[int64](3600),
}}, metav1.CreateOptions{})
if err != nil {
return nil, err
}
return &req.Status, nil
}
Loading

0 comments on commit d196f7b

Please sign in to comment.