Skip to content

Commit

Permalink
Merge branch 'master' into runtime_15_ksctl
Browse files Browse the repository at this point in the history
  • Loading branch information
ranakan19 authored Nov 11, 2024
2 parents 492a2bf + 8d63298 commit 1125cd0
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 10 deletions.
17 changes: 17 additions & 0 deletions pkg/assets/kubesaw-admins.go → pkg/assets/kubesaw_admins.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package assets

import "k8s.io/utils/strings/slices"

type KubeSawAdmins struct {
Clusters Clusters `yaml:"clusters"`
ServiceAccounts []ServiceAccount `yaml:"serviceAccounts"`
Expand Down Expand Up @@ -43,6 +45,21 @@ type ServiceAccount struct {
type Selector struct {
// SkipMembers can contain a list of member cluster names the entity shouldn't be applied for
SkipMembers []string `yaml:"skipMembers,omitempty"`
// MemberClusters defines a list of member cluster names the entity should be applied for
MemberClusters []string `yaml:"memberClusters,omitempty"`
}

func (s Selector) ShouldBeSkippedForMember(memberName string) bool {
// should be skipped if the specific member cluster name is provided
// and
// the name is listed in the skipped members
if memberName != "" && slices.Contains(s.SkipMembers, memberName) {
return true
}
// should be skipped if there is at least one selected member cluster
// and
// the name is either empty or is not specified in the selected member clusters
return len(s.MemberClusters) > 0 && (memberName == "" || !slices.Contains(s.MemberClusters, memberName))
}

type User struct {
Expand Down
66 changes: 66 additions & 0 deletions pkg/assets/kubesaw_admins_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package assets

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestShouldBeSkippedForMember1(t *testing.T) {
// given
member1Member2 := []string{"member1", "member2"}
member2Member3 := []string{"member2", "member3"}
testCases := map[string]struct {
s Selector
shouldBeSkipped bool
}{
"no selector": {s: Selector{}, shouldBeSkipped: false},
"different selected members": {s: Selector{MemberClusters: member2Member3}, shouldBeSkipped: true},
"in selected members": {s: Selector{MemberClusters: member1Member2}, shouldBeSkipped: false},
"listed in skipped members": {s: Selector{SkipMembers: member1Member2}, shouldBeSkipped: true},
"not listed in skipped members": {s: Selector{SkipMembers: member2Member3}, shouldBeSkipped: false},
"in selected members, but listed in skipped": {
s: Selector{MemberClusters: member1Member2, SkipMembers: member1Member2}, shouldBeSkipped: true},
"in selected members, not listed in skipped": {
s: Selector{MemberClusters: member1Member2, SkipMembers: member2Member3}, shouldBeSkipped: false},
"different selected members, not listed in skipped": {
s: Selector{MemberClusters: member2Member3, SkipMembers: member2Member3}, shouldBeSkipped: true},
"different selected members, and listed in skipped": {
s: Selector{MemberClusters: member2Member3, SkipMembers: member1Member2}, shouldBeSkipped: true},
}

for testName, data := range testCases {
t.Run(testName, func(t *testing.T) {
// when
shouldBeSkipped := data.s.ShouldBeSkippedForMember("member1")

// then
assert.Equal(t, data.shouldBeSkipped, shouldBeSkipped)
})
}
}

func TestShouldBeSkippedForEmptyName(t *testing.T) {
// given
member1Member2 := []string{"member1", "member2"}
testCases := map[string]struct {
s Selector
shouldBeSkipped bool
}{
"no selector": {s: Selector{}, shouldBeSkipped: false},
"some selected members": {s: Selector{MemberClusters: member1Member2}, shouldBeSkipped: true},
"some skipped members": {s: Selector{SkipMembers: member1Member2}, shouldBeSkipped: false},
"some selected members and some skipped members": {
s: Selector{MemberClusters: member1Member2, SkipMembers: member1Member2}, shouldBeSkipped: true},
}

for testName, data := range testCases {
t.Run(testName, func(t *testing.T) {
// when
shouldBeSkipped := data.s.ShouldBeSkippedForMember("")

// then
assert.Equal(t, data.shouldBeSkipped, shouldBeSkipped)
})
}
}
23 changes: 21 additions & 2 deletions pkg/cmd/generate/admin-manifests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,21 @@ func TestAdminManifests(t *testing.T) {
WithSkippedMembers("member2"),
Sa("bob", "",
HostRoleBindings("toolchain-host-operator", Role("restart-deployment"), ClusterRole("edit")),
MemberRoleBindings("toolchain-member-operator", Role("restart-deployment"), ClusterRole("edit")))),
MemberRoleBindings("toolchain-member-operator", Role("restart-deployment"), ClusterRole("edit"))),
Sa("jenny", "",
MemberRoleBindings("toolchain-member-operator", Role("restart-deployment"), ClusterRole("view"))).
WithSelectedMembers("member2")),
Users(
User("john-user", []string{"12345"}, false, "crtadmins-view",
HostRoleBindings("toolchain-host-operator", Role("register-cluster"), ClusterRole("edit")),
MemberRoleBindings("toolchain-member-operator", Role("register-cluster"), ClusterRole("edit"))).
WithSkippedMembers("member2"),
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")))))
MemberRoleBindings("toolchain-member-operator", Role("restart-deployment"), ClusterRole("admin"))),
User("jenny-crtadmin", []string{"98765"}, false, "crtadmins-exec",
MemberRoleBindings("toolchain-member-operator", Role("restart-deployment"), ClusterRole("view"))).
WithSelectedMembers("member2")))
kubeSawAdmins.DefaultServiceAccountsNamespace.Host = "kubesaw-sre-host"
kubeSawAdminsContent, err := yaml.Marshal(kubeSawAdmins)
require.NoError(t, err)
Expand Down Expand Up @@ -274,6 +280,11 @@ func verifyServiceAccounts(t *testing.T, outDir, expectedRootDir string, cluster
assertSa(saNs, "john").
hasRole(roleNs, clusterType.AsSuffix("install-operator"), clusterType.AsSuffix("install-operator-john")).
hasNsClusterRole(roleNs, "admin", clusterType.AsSuffix("clusterrole-admin-john"))
} else {
inKStructure(t, outDir, expectedRootDir).
assertSa(saNs, "jenny").
hasRole(roleNs, clusterType.AsSuffix("restart-deployment"), clusterType.AsSuffix("restart-deployment-jenny")).
hasNsClusterRole(roleNs, "view", clusterType.AsSuffix("clusterrole-view-jenny"))
}
inKStructure(t, outDir, expectedRootDir).
assertSa(saNs, "bob").
Expand Down Expand Up @@ -302,6 +313,14 @@ func verifyUsers(t *testing.T, outDir, expectedRootDir string, clusterType confi

// crtadmins-view group is not generated for member2 at all
bobsExtraGroupsUserIsNotPartOf = extraGroupsUserIsNotPartOf("crtadmins-view")
} else {
inKStructure(t, outDir, rootDir).
assertUser("jenny-crtadmin").
hasIdentity("98765")

newPermissionAssertion(storageAssertion, "", "jenny-crtadmin", "User").
hasRole(ns, clusterType.AsSuffix("restart-deployment"), clusterType.AsSuffix("restart-deployment-jenny-crtadmin")).
hasNsClusterRole(ns, "view", clusterType.AsSuffix("clusterrole-view-jenny-crtadmin"))
}

inKStructure(t, outDir, rootDir).
Expand Down
3 changes: 3 additions & 0 deletions pkg/cmd/generate/assertion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ func (a *kStructureAssertion) listObjects(dirName, kind string, object runtimecl
obj := object.DeepCopyObject()
objFile, err := os.ReadFile(path)
require.NoError(a.t, err)
assert.True(a.t, strings.HasPrefix(string(objFile), header), "every file generated by cli should container a header")
assert.NotContains(a.t, string(objFile), "creationTimestamp")
assert.NotContains(a.t, string(objFile), "user: {}")
err = yaml.Unmarshal(objFile, obj)
require.NoError(a.t, err)
if obj.GetObjectKind().GroupVersionKind().Kind == kind {
Expand Down
3 changes: 1 addition & 2 deletions pkg/cmd/generate/cli_configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"k8s.io/client-go/tools/clientcmd"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/utils/pointer"
"k8s.io/utils/strings/slices"
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
)

Expand Down Expand Up @@ -185,7 +184,7 @@ func generateForCluster(ctx *generateContext, clusterType configuration.ClusterT
tokenPerSAName := tokenPerSA{}

for _, sa := range ctx.kubeSawAdmins.ServiceAccounts {
if slices.Contains(sa.Selector.SkipMembers, clusterName) {
if clusterType == configuration.Member && sa.Selector.ShouldBeSkippedForMember(clusterName) {
continue
}
for saClusterType := range sa.PermissionsPerClusterType {
Expand Down
13 changes: 11 additions & 2 deletions pkg/cmd/generate/cli_configs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ func TestGenerateCliConfigs(t *testing.T) {
HostRoleBindings("toolchain-host-operator", Role("install-operator"), ClusterRole("admin")),
MemberRoleBindings("toolchain-member-operator", Role("install-operator"), ClusterRole("admin"))).
WithSkippedMembers("member2"),
Sa("jenny", "",
HostRoleBindings("toolchain-host-operator", Role("restart-deployment"), ClusterRole("view")),
MemberRoleBindings("toolchain-member-operator", Role("restart-deployment"), ClusterRole("view"))).
WithSelectedMembers("member2"),
Sa("bob", "",
HostRoleBindings("toolchain-host-operator", Role("restart=restart-deployment"), ClusterRole("restart=edit")),
MemberRoleBindings("toolchain-member-operator", Role("restart=restart-deployment"), ClusterRole("restart=edit")))),
Expand All @@ -54,13 +58,15 @@ func TestGenerateCliConfigs(t *testing.T) {
setupGockForServiceAccounts(t, HostServerAPI, 50,
newServiceAccount("kubesaw-sre-host", "john"),
newServiceAccount("kubesaw-sre-host", "bob"),
newServiceAccount("kubesaw-sre-host", "jenny"),
)
setupGockForServiceAccounts(t, Member1ServerAPI, 50,
newServiceAccount("kubesaw-admins-member", "john"),
newServiceAccount("kubesaw-admins-member", "bob"),
)
setupGockForServiceAccounts(t, Member2ServerAPI, 50,
newServiceAccount("kubesaw-admins-member", "bob"),
newServiceAccount("kubesaw-admins-member", "jenny"),
)
t.Cleanup(gock.OffAll)

Expand All @@ -87,7 +93,8 @@ func TestGenerateCliConfigs(t *testing.T) {

verifyKsctlConfigFiles(t, tempDir,
cliConfigForUser("john", hasHost(), hasMember("member1", "member1")),
cliConfigForUser("bob", hasHost(), hasMember("member1", "member1"), hasMember("member2", "member2")))
cliConfigForUser("bob", hasHost(), hasMember("member1", "member1"), hasMember("member2", "member2")),
cliConfigForUser("jenny", hasHost(), hasMember("member2", "member2")))
})

t.Run("when there SAs are defined for host cluster only", func(t *testing.T) {
Expand Down Expand Up @@ -127,6 +134,7 @@ func TestGenerateCliConfigs(t *testing.T) {
setupGockForServiceAccounts(t, HostServerAPI, 50,
newServiceAccount("kubesaw-admins-member", "john"),
newServiceAccount("kubesaw-admins-member", "bob"),
newServiceAccount("kubesaw-admins-member", "jenny"),
)
tempDir, err := os.MkdirTemp("", "ksctl-out-")
require.NoError(t, err)
Expand All @@ -141,7 +149,8 @@ func TestGenerateCliConfigs(t *testing.T) {

verifyKsctlConfigFiles(t, tempDir,
cliConfigForUser("john", hasHost(), hasMember("member1", "host")),
cliConfigForUser("bob", hasHost(), hasMember("member1", "host"), hasMember("member2", "host")))
cliConfigForUser("bob", hasHost(), hasMember("member1", "host"), hasMember("member2", "host")),
cliConfigForUser("jenny", hasHost(), hasMember("member2", "host")))
})
})

Expand Down
5 changes: 2 additions & 3 deletions pkg/cmd/generate/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package generate

import (
"github.com/kubesaw/ksctl/pkg/configuration"
"k8s.io/utils/strings/slices"
)

type clusterContext struct {
Expand All @@ -16,7 +15,7 @@ type clusterContext struct {
func ensureServiceAccounts(ctx *clusterContext, objsCache objectsCache) error {
ctx.Printlnf("-> Ensuring ServiceAccounts and its RoleBindings...")
for _, sa := range ctx.kubeSawAdmins.ServiceAccounts {
if ctx.specificKMemberName != "" && slices.Contains(sa.Selector.SkipMembers, ctx.specificKMemberName) {
if sa.Selector.ShouldBeSkippedForMember(ctx.specificKMemberName) {
continue
}

Expand Down Expand Up @@ -47,7 +46,7 @@ func ensureUsers(ctx *clusterContext, objsCache objectsCache) error {
ctx.Printlnf("-> Ensuring Users and its RoleBindings...")

for _, user := range ctx.kubeSawAdmins.Users {
if ctx.specificKMemberName != "" && slices.Contains(user.Selector.SkipMembers, ctx.specificKMemberName) {
if user.Selector.ShouldBeSkippedForMember(ctx.specificKMemberName) {
continue
}
m := &permissionsManager{
Expand Down
7 changes: 6 additions & 1 deletion pkg/cmd/generate/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"
"path/filepath"
"reflect"
"regexp"
"sort"
"strings"

Expand All @@ -19,6 +20,8 @@ import (
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
)

var emptyCreationTimestamp = regexp.MustCompile(`\n *creationTimestamp: null`)

type objectsCache map[string]runtimeclient.Object

func (c objectsCache) storeObject(ctx *clusterContext, obj runtimeclient.Object) error {
Expand Down Expand Up @@ -162,8 +165,10 @@ const header = `# --------------------------------------------------------------

func writeFile(filePath string, content []byte) error {
// https://github.com/kubernetes/kubernetes/issues/67610
contentString := strings.ReplaceAll(string(content), "\n creationTimestamp: null", "")
contentString := emptyCreationTimestamp.ReplaceAllString(string(content), "")
contentString = strings.ReplaceAll(contentString, "\nuser: {}", "")
// This will only apply to konflux tier template that start with metadata = {}
contentString = strings.ReplaceAll(contentString, "metadata:\n objects:", "metadata: {}\n objects:")
contentString = fmt.Sprintf("%s%s", header, contentString)
return os.WriteFile(filePath, []byte(contentString), 0600)
}
Expand Down
16 changes: 16 additions & 0 deletions pkg/test/environment_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ func (c ServiceAccountCreator) WithSkippedMembers(members ...string) ServiceAcco
}
}

func (c ServiceAccountCreator) WithSelectedMembers(members ...string) ServiceAccountCreator {
return func() assets.ServiceAccount {
serviceAccount := c()
serviceAccount.Selector.MemberClusters = members
return serviceAccount
}
}

func NewPermissionsPerClusterType(permissions ...PermissionsPerClusterTypeModifier) assets.PermissionsPerClusterType {
perm := map[string]assets.PermissionBindings{}
for _, addPermissions := range permissions {
Expand Down Expand Up @@ -179,3 +187,11 @@ func (c UserCreator) WithSkippedMembers(members ...string) UserCreator {
return user
}
}

func (c UserCreator) WithSelectedMembers(members ...string) UserCreator {
return func() assets.User {
user := c()
user.Selector.MemberClusters = members
return user
}
}

0 comments on commit 1125cd0

Please sign in to comment.