Skip to content

Commit 3957ccd

Browse files
CLOUDP-283292. AtlasCustomRole CRDs (#3450)
Co-authored-by: Helder Santana <[email protected]>
1 parent 8bdf685 commit 3957ccd

File tree

9 files changed

+545
-90
lines changed

9 files changed

+545
-90
lines changed

build/ci/library_owners.json

Lines changed: 62 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,64 @@
11
{
2-
"cloud.google.com/go/kms": "apix-2",
3-
"github.com/AlecAivazis/survey/v2": "apix-2",
4-
"github.com/Azure/azure-sdk-for-go/sdk/azcore": "apix-2",
5-
"github.com/Azure/azure-sdk-for-go/sdk/azidentity": "apix-2",
6-
"github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys": "apix-2",
7-
"github.com/Masterminds/semver/v3": "apix-2",
8-
"github.com/PaesslerAG/jsonpath": "apix-2",
9-
"github.com/aws/aws-sdk-go-v2": "apix-2",
10-
"github.com/aws/aws-sdk-go-v2/config": "apix-2",
11-
"github.com/aws/aws-sdk-go-v2/credentials": "apix-2",
12-
"github.com/aws/aws-sdk-go-v2/service/kms": "apix-2",
13-
"github.com/briandowns/spinner": "apix-2",
14-
"github.com/evergreen-ci/shrub": "apix-2",
15-
"github.com/go-test/deep": "apix-2",
16-
"github.com/golang-jwt/jwt/v4": "apix-2",
17-
"github.com/golang/mock": "apix-2",
18-
"github.com/google/go-github/v61": "apix-2",
19-
"github.com/google/uuid": "atlas_kubernetes_team",
20-
"github.com/klauspost/compress": "apix-2",
21-
"github.com/mattn/go-isatty": "apix-2",
22-
"github.com/mongodb-forks/digest": "apix-2",
23-
"github.com/mongodb-labs/cobra2snooty": "apix-2",
24-
"github.com/pelletier/go-toml": "apix-2",
25-
"github.com/Netflix/go-expect": "apix-2",
26-
"github.com/creack/pty": "apix-2",
27-
"github.com/hinshun/vt10x": "apix-2",
28-
"github.com/pkg/browser": "apix-2",
29-
"github.com/spf13/afero": "apix-2",
30-
"github.com/spf13/cobra": "apix-2",
31-
"github.com/spf13/pflag": "apix-2",
32-
"github.com/spf13/viper": "apix-2",
33-
"github.com/stretchr/testify": "apix-2",
34-
"github.com/tangzero/inflector": "apix-2",
35-
"go.mongodb.org/atlas": "apix-2",
36-
"go.mongodb.org/atlas-sdk/v20241113004": "apix-2",
37-
"go.mongodb.org/atlas-sdk/v20240530005": "apix-2",
38-
"go.mongodb.org/mongo-driver": "apix-2",
39-
"golang.org/x/sys": "apix-2",
40-
"golang.org/x/tools": "apix-2",
41-
"google.golang.org/api": "apix-2",
42-
"google.golang.org/protobuf": "apix-2",
43-
"golang.org/x/mod": "apix-2",
44-
"gopkg.in/yaml.v3": "apix-2",
45-
"github.com/mongodb/mongodb-atlas-kubernetes/v2": "atlas_kubernetes_team",
46-
"k8s.io/api": "atlas_kubernetes_team",
47-
"k8s.io/apimachinery": "atlas_kubernetes_team",
48-
"k8s.io/apiserver": "atlas_kubernetes_team",
49-
"k8s.io/client-go": "atlas_kubernetes_team",
50-
"k8s.io/apiextensions-apiserver": "atlas_kubernetes_team",
51-
"sigs.k8s.io/yaml": "atlas_kubernetes_team",
52-
"sigs.k8s.io/controller-runtime": "atlas_kubernetes_team",
53-
"sigs.k8s.io/kind": "atlas_kubernetes_team",
54-
"golang.org/x/exp": "atlas_kubernetes_team",
55-
"github.com/denisbrodbeck/machineid": "apix-2",
56-
"github.com/shirou/gopsutil/v4": "apix-2",
57-
"go.opentelemetry.io/otel": "apix-2",
58-
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc": "apix-2",
59-
"go.opentelemetry.io/otel/sdk": "apix-2",
60-
"go.opentelemetry.io/otel/trace": "apix-2",
61-
"google.golang.org/grpc": "apix-2",
62-
"github.com/mholt/archives": "apix-2"
2+
"cloud.google.com/go/kms": "apix-2",
3+
"github.com/AlecAivazis/survey/v2": "apix-2",
4+
"github.com/Azure/azure-sdk-for-go/sdk/azcore": "apix-2",
5+
"github.com/Azure/azure-sdk-for-go/sdk/azidentity": "apix-2",
6+
"github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys": "apix-2",
7+
"github.com/Masterminds/semver/v3": "apix-2",
8+
"github.com/PaesslerAG/jsonpath": "apix-2",
9+
"github.com/aws/aws-sdk-go-v2": "apix-2",
10+
"github.com/aws/aws-sdk-go-v2/config": "apix-2",
11+
"github.com/aws/aws-sdk-go-v2/credentials": "apix-2",
12+
"github.com/aws/aws-sdk-go-v2/service/kms": "apix-2",
13+
"github.com/briandowns/spinner": "apix-2",
14+
"github.com/evergreen-ci/shrub": "apix-2",
15+
"github.com/go-test/deep": "apix-2",
16+
"github.com/golang-jwt/jwt/v4": "apix-2",
17+
"github.com/golang/mock": "apix-2",
18+
"github.com/google/go-github/v61": "apix-2",
19+
"github.com/google/uuid": "atlas_kubernetes_team",
20+
"github.com/klauspost/compress": "apix-2",
21+
"github.com/mattn/go-isatty": "apix-2",
22+
"github.com/mongodb-forks/digest": "apix-2",
23+
"github.com/mongodb-labs/cobra2snooty": "apix-2",
24+
"github.com/pelletier/go-toml": "apix-2",
25+
"github.com/Netflix/go-expect": "apix-2",
26+
"github.com/creack/pty": "apix-2",
27+
"github.com/hinshun/vt10x": "apix-2",
28+
"github.com/pkg/browser": "apix-2",
29+
"github.com/spf13/afero": "apix-2",
30+
"github.com/spf13/cobra": "apix-2",
31+
"github.com/spf13/pflag": "apix-2",
32+
"github.com/spf13/viper": "apix-2",
33+
"github.com/stretchr/testify": "apix-2",
34+
"github.com/tangzero/inflector": "apix-2",
35+
"go.mongodb.org/atlas": "apix-2",
36+
"go.mongodb.org/atlas-sdk/v20241113004": "apix-2",
37+
"go.mongodb.org/atlas-sdk/v20240530005": "apix-2",
38+
"go.mongodb.org/atlas-sdk/v20241113001": "apix-2",
39+
"go.mongodb.org/mongo-driver": "apix-2",
40+
"golang.org/x/sys": "apix-2",
41+
"golang.org/x/tools": "apix-2",
42+
"google.golang.org/api": "apix-2",
43+
"google.golang.org/protobuf": "apix-2",
44+
"golang.org/x/mod": "apix-2",
45+
"gopkg.in/yaml.v3": "apix-2",
46+
"github.com/mongodb/mongodb-atlas-kubernetes/v2": "atlas_kubernetes_team",
47+
"k8s.io/api": "atlas_kubernetes_team",
48+
"k8s.io/apimachinery": "atlas_kubernetes_team",
49+
"k8s.io/apiserver": "atlas_kubernetes_team",
50+
"k8s.io/client-go": "atlas_kubernetes_team",
51+
"k8s.io/apiextensions-apiserver": "atlas_kubernetes_team",
52+
"sigs.k8s.io/yaml": "atlas_kubernetes_team",
53+
"sigs.k8s.io/controller-runtime": "atlas_kubernetes_team",
54+
"sigs.k8s.io/kind": "atlas_kubernetes_team",
55+
"golang.org/x/exp": "atlas_kubernetes_team",
56+
"github.com/denisbrodbeck/machineid": "apix-2",
57+
"github.com/shirou/gopsutil/v4": "apix-2",
58+
"go.opentelemetry.io/otel": "apix-2",
59+
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc": "apix-2",
60+
"go.opentelemetry.io/otel/sdk": "apix-2",
61+
"go.opentelemetry.io/otel/trace": "apix-2",
62+
"google.golang.org/grpc": "apix-2",
63+
"github.com/mholt/archives": "apix-2"
6364
}

internal/kubernetes/operator/config_exporter.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ func (e *ConfigExporter) WithIndependentResources(enabled bool) *ConfigExporter
123123
e.independentResources = enabled
124124
return e
125125
}
126-
127126
func (e *ConfigExporter) Run() (string, error) {
128127
// TODO: Add REST to OPERATOR entities matcher
129128
output := bytes.NewBufferString(yamlSeparator)
@@ -184,6 +183,7 @@ func (e *ConfigExporter) Run() (string, error) {
184183
return output.String(), nil
185184
}
186185

186+
//nolint:gocyclo
187187
func (e *ConfigExporter) exportProject() ([]runtime.Object, string, error) {
188188
atlasProject, err := e.dataProvider.Project(e.projectID)
189189
if err != nil {
@@ -256,6 +256,26 @@ func (e *ConfigExporter) exportProject() ([]runtime.Object, string, error) {
256256
}
257257
}
258258

259+
// Independent custom roles (AtlasCustomRole CR)
260+
if e.featureValidator.IsResourceSupported(features.ResourceAtlasCustomRole) {
261+
roles, err := project.BuildCustomRoles(e.dataProvider, project.CustomRolesRequest{
262+
ProjectID: e.projectID,
263+
ProjectName: projectData.Project.Name,
264+
TargetNamespace: e.targetNamespace,
265+
Version: e.operatorVersion,
266+
Credentials: credentialsName,
267+
IsIndependent: e.independentResources,
268+
Dict: e.dictionaryForAtlasNames,
269+
})
270+
if err != nil {
271+
return nil, "", err
272+
}
273+
274+
for i := range len(roles) {
275+
r = append(r, &roles[i])
276+
}
277+
}
278+
259279
// DB users
260280
usersData, relatedSecrets, err := dbusers.BuildDBUsers(
261281
e.dataProvider,

internal/kubernetes/operator/features/crds.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const (
4242
ResourceAtlasStreamConnection = "atlasstreamconnections"
4343
ResourceAtlasBackupCompliancePolicy = "atlasbackupcompliancepolicies"
4444
ResourceAtlasPrivateEndpoint = "atlasprivateendpoints"
45+
ResourceAtlasCustomRole = "atlascustomroles"
4546
)
4647

4748
var (
@@ -92,6 +93,7 @@ var (
9293
resource{ResourceAtlasStreamConnection, NopPatcher()},
9394
resource{ResourceAtlasBackupCompliancePolicy, NopPatcher()},
9495
resource{ResourceAtlasPrivateEndpoint, NopPatcher()},
96+
resource{ResourceAtlasCustomRole, NopPatcher()},
9597
},
9698
}
9799
)

internal/kubernetes/operator/features/validator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ package features
1818

1919
type FeatureValidator interface {
2020
IsResourceSupported(resourceName string) bool
21-
FeatureExist(resourceName, version string) bool
21+
FeatureExist(resourceName, path string) bool
2222
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Copyright 2024 MongoDB Inc
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package project
16+
17+
import (
18+
"fmt"
19+
20+
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/kubernetes/operator/features"
21+
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/kubernetes/operator/resources"
22+
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/pointer"
23+
"github.com/mongodb/mongodb-atlas-cli/atlascli/internal/store"
24+
akoapi "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/api"
25+
akov2 "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/api/v1"
26+
akov2common "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/api/v1/common"
27+
akov2status "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/api/v1/status"
28+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
)
30+
31+
type CustomRolesRequest struct {
32+
ProjectName string
33+
ProjectID string
34+
TargetNamespace string
35+
Credentials string
36+
Version string
37+
IsIndependent bool
38+
Dict map[string]string
39+
}
40+
41+
func BuildCustomRoles(provider store.DatabaseRoleLister, request CustomRolesRequest) ([]akov2.AtlasCustomRole, error) {
42+
roles, err := provider.DatabaseRoles(request.ProjectID)
43+
if err != nil {
44+
return nil, err
45+
}
46+
if roles == nil {
47+
return nil, nil
48+
}
49+
50+
result := make([]akov2.AtlasCustomRole, 0, len(roles))
51+
52+
for rIdx := range roles {
53+
role := &roles[rIdx]
54+
55+
inhRoles := make([]akov2.Role, 0, len(role.GetInheritedRoles()))
56+
for _, rl := range role.GetInheritedRoles() {
57+
inhRoles = append(inhRoles, akov2.Role{
58+
Name: rl.Role,
59+
Database: rl.Db,
60+
})
61+
}
62+
63+
actions := make([]akov2.Action, 0, len(role.GetActions()))
64+
for _, action := range role.GetActions() {
65+
r := make([]akov2.Resource, 0, len(action.GetResources()))
66+
for _, res := range action.GetResources() {
67+
r = append(r, akov2.Resource{
68+
Cluster: pointer.Get(res.Cluster),
69+
Database: pointer.Get(res.Db),
70+
Collection: pointer.Get(res.Collection),
71+
})
72+
}
73+
actions = append(actions, akov2.Action{
74+
Name: action.Action,
75+
Resources: r,
76+
})
77+
}
78+
79+
akoRole := akov2.AtlasCustomRole{
80+
TypeMeta: metav1.TypeMeta{
81+
Kind: "AtlasCustomRole",
82+
APIVersion: "atlas.mongodb.com/v1",
83+
},
84+
ObjectMeta: metav1.ObjectMeta{
85+
Name: resources.NormalizeAtlasName(
86+
fmt.Sprintf("%s-custom-role-%s",
87+
request.ProjectName,
88+
role.RoleName),
89+
request.Dict),
90+
Namespace: request.TargetNamespace,
91+
Labels: map[string]string{
92+
features.ResourceVersion: request.Version,
93+
},
94+
},
95+
Spec: akov2.AtlasCustomRoleSpec{
96+
Role: akov2.CustomRole{
97+
Name: role.RoleName,
98+
InheritedRoles: inhRoles,
99+
Actions: actions,
100+
},
101+
},
102+
Status: akov2status.AtlasCustomRoleStatus{
103+
Common: akoapi.Common{Conditions: []akoapi.Condition{}},
104+
},
105+
}
106+
if request.IsIndependent {
107+
akoRole.Spec.ExternalProjectIDRef = &akov2.ExternalProjectReference{
108+
ID: request.ProjectID,
109+
}
110+
akoRole.Spec.LocalCredentialHolder = akoapi.LocalCredentialHolder{
111+
ConnectionSecret: &akoapi.LocalObjectReference{
112+
Name: resources.NormalizeAtlasName(request.Credentials, request.Dict),
113+
},
114+
}
115+
} else {
116+
akoRole.Spec.ProjectRef = &akov2common.ResourceRefNamespaced{
117+
Name: request.ProjectName,
118+
Namespace: request.TargetNamespace,
119+
}
120+
}
121+
result = append(result, akoRole)
122+
}
123+
return result, nil
124+
}

0 commit comments

Comments
 (0)