Skip to content

Commit

Permalink
default ServiceAccounts namespace names (#72)
Browse files Browse the repository at this point in the history
  • Loading branch information
MatousJobanek authored Sep 12, 2024
1 parent 5b6d495 commit 1fbffd4
Show file tree
Hide file tree
Showing 14 changed files with 151 additions and 49 deletions.
3 changes: 3 additions & 0 deletions pkg/assets/assets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ func TestGetKubeSawAdmins(t *testing.T) {
assert.NotEmpty(t, member.API)
}

assert.Equal(t, "host-sre-namespace", kubeSawAdmins.DefaultServiceAccountsNamespace.Host)
assert.Equal(t, "member-sre-namespace", kubeSawAdmins.DefaultServiceAccountsNamespace.Member)

assert.NotEmpty(t, kubeSawAdmins.ServiceAccounts)
for _, sa := range kubeSawAdmins.ServiceAccounts {
assert.NotEmpty(t, sa.Name)
Expand Down
14 changes: 11 additions & 3 deletions pkg/assets/kubesaw-admins.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package assets

type KubeSawAdmins struct {
Clusters Clusters `yaml:"clusters"`
ServiceAccounts []ServiceAccount `yaml:"serviceAccounts"`
Users []User `yaml:"users"`
Clusters Clusters `yaml:"clusters"`
ServiceAccounts []ServiceAccount `yaml:"serviceAccounts"`
Users []User `yaml:"users"`
DefaultServiceAccountsNamespace DefaultServiceAccountsNamespace `yaml:"defaultServiceAccountsNamespace"`
}

type Clusters struct {
Expand All @@ -24,6 +25,13 @@ type ClusterConfig struct {
API string `yaml:"api"`
}

// DefaultServiceAccountsNamespace defines the names of the default namespaces where the ksctl SAs should be created.
// If not specified, then the names kubesaw-admins-host and kubesaw-admins-member are used.
type DefaultServiceAccountsNamespace struct {
Host string `yaml:"host"`
Member string `yaml:"member"`
}

type ServiceAccount struct {
Name string `yaml:"name"`
Namespace string `yaml:"namespace,omitempty"`
Expand Down
4 changes: 4 additions & 0 deletions pkg/cmd/generate/admin-manifests.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ func adminManifests(term ioutils.Terminal, files assets.FS, flags adminManifests
}
}
}

if defaultSAsNamespace(kubeSawAdmins, configuration.Host) == defaultSAsNamespace(kubeSawAdmins, configuration.Member) {
return fmt.Errorf("the default ServiceAccounts namespace has the same name for host cluster as for the member clusters (%s), they have to be different", defaultSAsNamespace(kubeSawAdmins, configuration.Host))
}
err = os.RemoveAll(flags.outDir)
if err != nil {
return err
Expand Down
38 changes: 36 additions & 2 deletions pkg/cmd/generate/admin-manifests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestAdminManifests(t *testing.T) {
User("bob-crtadmin", []string{"67890"}, false, "crtadmins-exec",
HostRoleBindings("toolchain-host-operator", Role("restart-deployment"), ClusterRole("admin")),
MemberRoleBindings("toolchain-member-operator", Role("restart-deployment"), ClusterRole("admin")))))

kubeSawAdmins.DefaultServiceAccountsNamespace.Host = "kubesaw-sre-host"
kubeSawAdminsContent, err := yaml.Marshal(kubeSawAdmins)
require.NoError(t, err)

Expand Down Expand Up @@ -85,6 +85,9 @@ func TestAdminManifests(t *testing.T) {
t.Run("without separateKustomizeComponent set for member2", func(t *testing.T) {
// given
kubeSawAdmins.Clusters.Members[1].SeparateKustomizeComponent = false
t.Cleanup(func() {
kubeSawAdmins.Clusters.Members[1].SeparateKustomizeComponent = true
})
kubeSawAdminsContent, err := yaml.Marshal(kubeSawAdmins)
require.NoError(t, err)

Expand Down Expand Up @@ -184,6 +187,34 @@ func TestAdminManifests(t *testing.T) {
// then
require.Error(t, err)
})

t.Run("when default SAs namespace names are the same, then fail", func(t *testing.T) {
// given
kubeSawAdmins.DefaultServiceAccountsNamespace.Host = "kubesaw-sre"
kubeSawAdmins.DefaultServiceAccountsNamespace.Member = "kubesaw-sre"
t.Cleanup(func() {
kubeSawAdmins.DefaultServiceAccountsNamespace.Host = "kubesaw-sre-host"
kubeSawAdmins.DefaultServiceAccountsNamespace.Member = ""
})
kubeSawAdminsContent, err := yaml.Marshal(kubeSawAdmins)
require.NoError(t, err)

configFile := createKubeSawAdminsFile(t, "kubesaw.host.openshiftapps.com", kubeSawAdminsContent)
files := newDefaultFiles(t)

outTempDir, err := os.MkdirTemp("", "admin-manifests-cli-test-")
require.NoError(t, err)
term := NewFakeTerminalWithResponse("Y")
term.Tee(os.Stdout)
flags := newAdminManifestsFlags(outDir(outTempDir), kubeSawAdminsFile(configFile))

// when
err = adminManifests(term, files, flags)

// then
require.EqualError(t, err, "the default ServiceAccounts namespace has the same name for host cluster as for the member clusters (kubesaw-sre), they have to be different")
})

}

func storeDummySA(t *testing.T, outDir string) {
Expand Down Expand Up @@ -232,7 +263,10 @@ func verifyFiles(t *testing.T, flags adminManifestsFlags) {
}

func verifyServiceAccounts(t *testing.T, outDir, expectedRootDir string, clusterType configuration.ClusterType, roleNs string) {
saNs := fmt.Sprintf("sandbox-sre-%s", clusterType)
saNs := "kubesaw-sre-host"
if clusterType == configuration.Member {
saNs = "kubesaw-admins-member"
}

if expectedRootDir != "member2" {
// john is skipped for member2 (when generated as a separate kustomize component)
Expand Down
4 changes: 2 additions & 2 deletions pkg/cmd/generate/cli_configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func generateForCluster(ctx *generateContext, clusterType configuration.ClusterT
ctx.PrintContextSeparatorf("Generating the content of the ksctl.yaml files for %s cluster running at %s", clusterName, clusterSpec.API)

// find config we can build client for the cluster from
externalClient, err := buildClientFromKubeconfigFiles(ctx, clusterSpec.API, ctx.kubeconfigPaths, sandboxSRENamespace(clusterType))
externalClient, err := buildClientFromKubeconfigFiles(ctx, clusterSpec.API, ctx.kubeconfigPaths, defaultSAsNamespace(ctx.kubeSawAdmins, clusterType))
if err != nil {
return err
}
Expand All @@ -192,7 +192,7 @@ func generateForCluster(ctx *generateContext, clusterType configuration.ClusterT
if saClusterType != clusterType.String() {
continue
}
saNamespace := sandboxSRENamespace(clusterType)
saNamespace := defaultSAsNamespace(ctx.kubeSawAdmins, clusterType)
if sa.Namespace != "" {
saNamespace = sa.Namespace
}
Expand Down
38 changes: 21 additions & 17 deletions pkg/cmd/generate/cli_configs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"testing"

"github.com/h2non/gock"
"github.com/kubesaw/ksctl/pkg/assets"
"github.com/kubesaw/ksctl/pkg/client"
"github.com/kubesaw/ksctl/pkg/configuration"
. "github.com/kubesaw/ksctl/pkg/test"
Expand Down Expand Up @@ -40,25 +41,26 @@ func TestGenerateCliConfigs(t *testing.T) {
HostRoleBindings("toolchain-host-operator", Role("restart=restart-deployment"), ClusterRole("restart=edit")),
MemberRoleBindings("toolchain-member-operator", Role("restart=restart-deployment"), ClusterRole("restart=edit")))),
Users())
kubeSawAdmins.DefaultServiceAccountsNamespace.Host = "kubesaw-sre-host"

kubeSawAdminsContent, err := yaml.Marshal(kubeSawAdmins)
require.NoError(t, err)
kubeconfigFiles := createKubeconfigFiles(t, ksctlKubeconfigContent, ksctlKubeconfigContentMember2)

setupGockForListServiceAccounts(t, HostServerAPI, configuration.Host)
setupGockForListServiceAccounts(t, Member1ServerAPI, configuration.Member)
setupGockForListServiceAccounts(t, Member2ServerAPI, configuration.Member)
setupGockForListServiceAccounts(t, kubeSawAdmins, HostServerAPI, configuration.Host)
setupGockForListServiceAccounts(t, kubeSawAdmins, Member1ServerAPI, configuration.Member)
setupGockForListServiceAccounts(t, kubeSawAdmins, Member2ServerAPI, configuration.Member)

setupGockForServiceAccounts(t, HostServerAPI, 50,
newServiceAccount("sandbox-sre-host", "john"),
newServiceAccount("sandbox-sre-host", "bob"),
newServiceAccount("kubesaw-sre-host", "john"),
newServiceAccount("kubesaw-sre-host", "bob"),
)
setupGockForServiceAccounts(t, Member1ServerAPI, 50,
newServiceAccount("sandbox-sre-member", "john"),
newServiceAccount("sandbox-sre-member", "bob"),
newServiceAccount("kubesaw-admins-member", "john"),
newServiceAccount("kubesaw-admins-member", "bob"),
)
setupGockForServiceAccounts(t, Member2ServerAPI, 50,
newServiceAccount("sandbox-sre-member", "bob"),
newServiceAccount("kubesaw-admins-member", "bob"),
)
t.Cleanup(gock.OffAll)

Expand Down Expand Up @@ -100,6 +102,7 @@ func TestGenerateCliConfigs(t *testing.T) {
Sa("bob", "",
HostRoleBindings("toolchain-host-operator", Role("restart=restart-deployment"), ClusterRole("restart=edit")))),
Users())
saInHostOnly.DefaultServiceAccountsNamespace.Host = "kubesaw-sre-host"
kubeSawAdminsContent, err := yaml.Marshal(saInHostOnly)
require.NoError(t, err)
configFile := createKubeSawAdminsFile(t, "kubesaw.host.openshiftapps.com", kubeSawAdminsContent)
Expand All @@ -120,10 +123,10 @@ func TestGenerateCliConfigs(t *testing.T) {

t.Run("in dev mode", func(t *testing.T) {
// given
setupGockForListServiceAccounts(t, HostServerAPI, configuration.Member)
setupGockForListServiceAccounts(t, kubeSawAdmins, HostServerAPI, configuration.Member)
setupGockForServiceAccounts(t, HostServerAPI, 50,
newServiceAccount("sandbox-sre-member", "john"),
newServiceAccount("sandbox-sre-member", "bob"),
newServiceAccount("kubesaw-admins-member", "john"),
newServiceAccount("kubesaw-admins-member", "bob"),
)
tempDir, err := os.MkdirTemp("", "ksctl-out-")
require.NoError(t, err)
Expand Down Expand Up @@ -153,7 +156,7 @@ func TestGenerateCliConfigs(t *testing.T) {
}

// when
_, err := buildClientFromKubeconfigFiles(ctx, "https://dummy.openshift.com", kubeconfigFiles, sandboxSRENamespace(configuration.Host))
_, err := buildClientFromKubeconfigFiles(ctx, "https://dummy.openshift.com", kubeconfigFiles, defaultSAsNamespace(kubeSawAdmins, configuration.Host))

// then
require.Error(t, err)
Expand All @@ -162,7 +165,7 @@ func TestGenerateCliConfigs(t *testing.T) {

t.Run("test buildClientFromKubeconfigFiles cannot list service accounts", func(t *testing.T) {
// given
path := fmt.Sprintf("api/v1/namespaces/%s/serviceaccounts/", sandboxSRENamespace(configuration.Host))
path := fmt.Sprintf("api/v1/namespaces/%s/serviceaccounts/", defaultSAsNamespace(kubeSawAdmins, configuration.Host))
gock.New("https://dummy.openshift.com").Get(path).Persist().Reply(403)
ctx := &generateContext{
Terminal: term,
Expand All @@ -173,7 +176,7 @@ func TestGenerateCliConfigs(t *testing.T) {
}

// when
_, err := buildClientFromKubeconfigFiles(ctx, "https://dummy.openshift.com", kubeconfigFiles, sandboxSRENamespace(configuration.Host))
_, err := buildClientFromKubeconfigFiles(ctx, "https://dummy.openshift.com", kubeconfigFiles, defaultSAsNamespace(kubeSawAdmins, configuration.Host))

// then
require.Error(t, err)
Expand Down Expand Up @@ -216,6 +219,7 @@ func TestGenerateCliConfigs(t *testing.T) {
Sa("notmocked", "",
HostRoleBindings("toolchain-host-operator", Role("install-operator"), ClusterRole("admin")))),
Users())
saInHostOnly.DefaultServiceAccountsNamespace.Host = "kubesaw-sre-host"
kubeSawAdminsContent, err := yaml.Marshal(saInHostOnly)
require.NoError(t, err)
configFile := createKubeSawAdminsFile(t, "kubesaw.host.openshiftapps.com", kubeSawAdminsContent)
Expand Down Expand Up @@ -347,22 +351,22 @@ func (a *ksctlConfigAssertion) hasCluster(clusterName, subDomain string, cluster
assert.Equal(a.t, fmt.Sprintf("token-secret-for-%s", a.saBaseName), a.ksctlConfig.ClusterAccessDefinitions[clusterName].Token)
}

func setupGockForListServiceAccounts(t *testing.T, apiEndpoint string, clusterType configuration.ClusterType) {
func setupGockForListServiceAccounts(t *testing.T, kubeSawAdmins *assets.KubeSawAdmins, apiEndpoint string, clusterType configuration.ClusterType) {
resultServiceAccounts := &corev1.ServiceAccountList{
TypeMeta: metav1.TypeMeta{},
ListMeta: metav1.ListMeta{},
Items: []corev1.ServiceAccount{
{
ObjectMeta: metav1.ObjectMeta{
Namespace: sandboxSRENamespace(clusterType),
Namespace: defaultSAsNamespace(kubeSawAdmins, clusterType),
Name: clusterType.String(),
},
},
},
}
resultServiceAccountsStr, err := json.Marshal(resultServiceAccounts)
require.NoError(t, err)
path := fmt.Sprintf("api/v1/namespaces/%s/serviceaccounts/", sandboxSRENamespace(clusterType))
path := fmt.Sprintf("api/v1/namespaces/%s/serviceaccounts/", defaultSAsNamespace(kubeSawAdmins, clusterType))
t.Logf("mocking access to List %s/%s", apiEndpoint, path)
gock.New(apiEndpoint).
Get(path).
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/generate/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func ensureUsers(ctx *clusterContext, objsCache objectsCache) error {
}
// create the subject if explicitly requested (even if there is no specific permissions)
if user.AllClusters {
if _, err := m.createSubject(ctx, m.objectsCache, m.subjectBaseName, sandboxSRENamespace(ctx.clusterType), ksctlLabelsWithUsername(m.subjectBaseName)); err != nil {
if _, err := m.createSubject(ctx, m.objectsCache, m.subjectBaseName, defaultSAsNamespace(ctx.kubeSawAdmins, ctx.clusterType), ksctlLabelsWithUsername(m.subjectBaseName)); err != nil {
return err
}
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/cmd/generate/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestEnsureServiceAccounts(t *testing.T) {
require.NoError(t, err)

roleNs := fmt.Sprintf("toolchain-%s-operator", clusterType)
saNs := fmt.Sprintf("sandbox-sre-%s", clusterType)
saNs := fmt.Sprintf("kubesaw-admins-%s", clusterType)

inObjectCache(t, ctx.outDir, clusterType.String(), cache).
assertSa(saNs, "john").
Expand Down Expand Up @@ -119,7 +119,7 @@ func TestEnsureServiceAccounts(t *testing.T) {
assertNumberOfRoles(1)

inObjectCache(t, ctx.outDir, "member-1", cache).
assertSa("sandbox-sre-member", "bob").
assertSa("kubesaw-admins-member", "bob").
hasRole("toolchain-member-operator", configuration.Member.AsSuffix("restart-deployment"), configuration.Member.AsSuffix("restart-deployment-bob")).
hasNsClusterRole("toolchain-member-operator", "view", configuration.Member.AsSuffix("clusterrole-view-bob"))
})
Expand Down
8 changes: 3 additions & 5 deletions pkg/cmd/generate/mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,12 @@ const (

// files part

func newDefaultFiles(t *testing.T, fakeFiles ...test.FakeFileCreator) assets.FS {
func newDefaultFiles(t *testing.T) assets.FS {
roles := []runtime.Object{installOperatorRole, restartDeploymentRole, editDeploymentRole, registerClusterRole}

files := test.NewFakeFiles(t,
append(fakeFiles,
test.FakeTemplate("roles/host.yaml", roles...),
test.FakeTemplate("roles/member.yaml", roles...))...,
)
test.FakeTemplate("roles/host.yaml", roles...),
test.FakeTemplate("roles/member.yaml", roles...))
return files
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/generate/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (m *permissionsManager) ensurePermission(ctx *clusterContext, roleName, tar
}

// ensure that the subject exists
subject, err := m.createSubject(ctx, m.objectsCache, m.subjectBaseName, sandboxSRENamespace(ctx.clusterType), ksctlLabelsWithUsername(m.subjectBaseName))
subject, err := m.createSubject(ctx, m.objectsCache, m.subjectBaseName, defaultSAsNamespace(ctx.kubeSawAdmins, ctx.clusterType), ksctlLabelsWithUsername(m.subjectBaseName))
if err != nil {
return err
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/cmd/generate/permissions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func TestEnsurePermissionsInNamespaces(t *testing.T) {
// then
require.NoError(t, err)
roleNs := fmt.Sprintf("toolchain-%s-operator", clusterType)
saNs := fmt.Sprintf("sandbox-sre-%s", clusterType)
saNs := fmt.Sprintf("kubesaw-admins-%s", clusterType)

inObjectCache(t, ctx.outDir, clusterType.String(), permManager.objectsCache).
assertSa(saNs, "john").
Expand Down Expand Up @@ -91,15 +91,15 @@ func TestEnsureServiceAccount(t *testing.T) {

// when
subject, err := ensureServiceAccount("")(
ctx, cache, "john", "sandbox-sre-host", labels)
ctx, cache, "john", "kubesaw-admins-host", labels)

// then
require.NoError(t, err)
inObjectCache(t, ctx.outDir, "host", cache).
assertSa("sandbox-sre-host", "john")
assertSa("kubesaw-admins-host", "john")
assert.Equal(t, "ServiceAccount", subject.Kind)
assert.Equal(t, "john", subject.Name)
assert.Equal(t, "sandbox-sre-host", subject.Namespace)
assert.Equal(t, "kubesaw-admins-host", subject.Namespace)
})

t.Run("create SA in the given namespace", func(t *testing.T) {
Expand Down
14 changes: 10 additions & 4 deletions pkg/cmd/generate/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

"github.com/ghodss/yaml"
"github.com/kubesaw/ksctl/pkg/assets"
"github.com/kubesaw/ksctl/pkg/configuration"
"github.com/kubesaw/ksctl/pkg/utils"
"k8s.io/apimachinery/pkg/api/meta"
Expand Down Expand Up @@ -231,10 +232,15 @@ func ksctlLabels() map[string]string {
}
}

func sandboxSRENamespace(clusterType configuration.ClusterType) string {
sandboxSRENamespace := "sandbox-sre-host"
func defaultSAsNamespace(kubeSawAdmins *assets.KubeSawAdmins, clusterType configuration.ClusterType) string {
if clusterType == configuration.Member {
sandboxSRENamespace = "sandbox-sre-member"
if kubeSawAdmins.DefaultServiceAccountsNamespace.Member != "" {
return kubeSawAdmins.DefaultServiceAccountsNamespace.Member
}
return "kubesaw-admins-member"
}
if kubeSawAdmins.DefaultServiceAccountsNamespace.Host != "" {
return kubeSawAdmins.DefaultServiceAccountsNamespace.Host
}
return sandboxSRENamespace
return "kubesaw-admins-host"
}
Loading

0 comments on commit 1fbffd4

Please sign in to comment.