Skip to content

Commit 885b39f

Browse files
Get agent tentacle namespaces
1 parent b2edc1f commit 885b39f

File tree

4 files changed

+145
-4
lines changed

4 files changed

+145
-4
lines changed

cmd/main.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,15 @@ func main() {
205205
os.Exit(1)
206206
}
207207

208+
// Discover target namespaces from octopus tentacle deployments
209+
targetNamespaces, err := rules.DiscoverTargetNamespaces(mgr.GetClient())
210+
if err != nil {
211+
setupLog.Error(err, "unable to discover target namespaces")
212+
os.Exit(1)
213+
}
214+
208215
// Create the rules engine instance
209-
engine := rules.NewInMemoryEngine()
216+
engine := rules.NewInMemoryEngine(targetNamespaces)
210217

211218
if err := (&controller.WorkloadServiceAccountReconciler{
212219
Client: mgr.GetClient(),

internal/rules/engine.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,19 @@ type Engine interface {
2929
}
3030

3131
type InMemoryEngine struct {
32-
rules map[AgentName]map[Scope]ServiceAccountName
32+
rules map[AgentName]map[Scope]ServiceAccountName
33+
targetNamespaces []string
3334
// client kubernetes.Interface
3435
}
3536

3637
func (s *Scope) IsEmpty() bool {
3738
return s.Project == "" && s.Environment == "" && s.Tenant == "" && s.Step == "" && s.Space == ""
3839
}
3940

40-
func NewInMemoryEngine() InMemoryEngine {
41+
func NewInMemoryEngine(targetNamespaces []string) InMemoryEngine {
4142
return InMemoryEngine{
42-
rules: make(map[AgentName]map[Scope]ServiceAccountName),
43+
rules: make(map[AgentName]map[Scope]ServiceAccountName),
44+
targetNamespaces: targetNamespaces,
4345
}
4446
}
4547

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package rules
2+
3+
import (
4+
"context"
5+
6+
appsv1 "k8s.io/api/apps/v1"
7+
ctrl "sigs.k8s.io/controller-runtime"
8+
"sigs.k8s.io/controller-runtime/pkg/client"
9+
)
10+
11+
var discoveryLog = ctrl.Log.WithName("namespace_discovery")
12+
13+
// DiscoverTargetNamespaces finds namespaces containing Octopus Tentacle deployments
14+
// these namespaces are used for creating the service accounts for use by pods deployed by Tentacle those namespaces
15+
func DiscoverTargetNamespaces(k8sClient client.Client) ([]string, error) {
16+
ctx := context.Background()
17+
18+
var deployments appsv1.DeploymentList
19+
err := k8sClient.List(ctx, &deployments, client.MatchingLabels{
20+
"app.kubernetes.io/name": "octopus-agent",
21+
})
22+
if err != nil {
23+
discoveryLog.Error(err, "Failed to list deployments with octopus agent label")
24+
return nil, err
25+
}
26+
27+
namespaceSet := make(map[string]struct{})
28+
for _, deployment := range deployments.Items {
29+
namespaceSet[deployment.Namespace] = struct{}{}
30+
}
31+
32+
namespaces := make([]string, 0, len(namespaceSet))
33+
for namespace := range namespaceSet {
34+
namespaces = append(namespaces, namespace)
35+
}
36+
37+
// Use default namespace as fallback for local testing if no tentacle deployments found
38+
if len(namespaces) == 0 {
39+
namespaces = []string{"default"}
40+
discoveryLog.Info("No octopus tentacle deployments found, using default namespace as fallback")
41+
} else {
42+
discoveryLog.Info("Discovered target namespaces for service account creation",
43+
"count", len(namespaces),
44+
"namespaces", namespaces)
45+
}
46+
47+
return namespaces, nil
48+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package rules
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
appsv1 "k8s.io/api/apps/v1"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
"k8s.io/apimachinery/pkg/runtime"
10+
"sigs.k8s.io/controller-runtime/pkg/client"
11+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
12+
)
13+
14+
func TestDiscoverTargetNamespaces(t *testing.T) {
15+
scheme := runtime.NewScheme()
16+
assert.NoError(t, appsv1.AddToScheme(scheme))
17+
18+
tests := []struct {
19+
name string
20+
deployments []appsv1.Deployment
21+
expected []string
22+
}{
23+
{
24+
name: "no deployments should return default namespace",
25+
deployments: []appsv1.Deployment{},
26+
expected: []string{"default"},
27+
},
28+
{
29+
name: "multiple deployments with duplicate namespace should return unique namespaces",
30+
deployments: []appsv1.Deployment{
31+
{
32+
ObjectMeta: metav1.ObjectMeta{
33+
Name: "octopus-agent-1",
34+
Namespace: "test-namespace",
35+
Labels: map[string]string{
36+
"app.kubernetes.io/name": "octopus-agent",
37+
},
38+
},
39+
},
40+
{
41+
ObjectMeta: metav1.ObjectMeta{
42+
Name: "octopus-agent-2",
43+
Namespace: "test-namespace", // duplicate namespace
44+
Labels: map[string]string{
45+
"app.kubernetes.io/name": "octopus-agent",
46+
},
47+
},
48+
},
49+
{
50+
ObjectMeta: metav1.ObjectMeta{
51+
Name: "octopus-agent-3",
52+
Namespace: "octopus-system",
53+
Labels: map[string]string{
54+
"app.kubernetes.io/name": "octopus-agent",
55+
},
56+
},
57+
},
58+
},
59+
expected: []string{"octopus-system", "test-namespace"}, // should be sorted and unique
60+
},
61+
}
62+
63+
for _, tt := range tests {
64+
t.Run(tt.name, func(t *testing.T) {
65+
// Create fake client with test deployments
66+
var objects []client.Object
67+
for i := range tt.deployments {
68+
objects = append(objects, &tt.deployments[i])
69+
}
70+
71+
fakeClient := fake.NewClientBuilder().
72+
WithScheme(scheme).
73+
WithObjects(objects...).
74+
Build()
75+
76+
// Test the function
77+
result, err := DiscoverTargetNamespaces(fakeClient)
78+
79+
// Assertions
80+
assert.NoError(t, err)
81+
assert.ElementsMatch(t, tt.expected, result)
82+
})
83+
}
84+
}

0 commit comments

Comments
 (0)