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 08f272f commit 35a39b9
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 51 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 @@ -32,8 +33,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 @@ -44,6 +48,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/metrics/server"

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 @@ -201,18 +206,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 @@ -29,8 +30,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 @@ -40,6 +44,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/metrics/server"

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 @@ -193,18 +198,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(),
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
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,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 @@ -220,3 +220,5 @@ require (
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 => ../helm-operator-plugins
10 changes: 4 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,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 +414,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
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) {
k.getLock(key).Lock()
}

func (k keyLock[K]) Unlock(key K) {
k.getLock(key).Unlock()
}

func (k keyLock[K]) getLock(key K) *sync.Mutex {
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 35a39b9

Please sign in to comment.