Skip to content

Commit f8629f7

Browse files
DBAAS-972 Grant access to the newly created database instance in Atlas Operator
1 parent cc41886 commit f8629f7

File tree

6 files changed

+125
-11
lines changed

6 files changed

+125
-11
lines changed

config/dbaasprovider/dbaas_provider.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,7 @@ spec:
6464
type: string
6565
required: false
6666
defaultValue: M0
67+
- name: ipAccessList
68+
displayName: List of IP Addresses or Ranges (Space Separated) to Access the Cluster
69+
type: string
70+
required: false

pkg/api/dbaas/mongodbatlasconnection_types.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ import (
1919
)
2020

2121
const (
22-
CloudProviderKey = "providerName"
23-
CloudRegionKey = "regionName"
24-
ProjectIDKey = "projectID"
25-
ProjectNameKey = "projectName"
26-
ProvisionPhaseKey = "state"
27-
InstanceSizeNameKey = "instanceSizeName"
28-
ClusterNameKey = "clusterName"
29-
22+
CloudProviderKey = "providerName"
23+
CloudRegionKey = "regionName"
24+
ProjectIDKey = "projectID"
25+
ProjectNameKey = "projectName"
26+
ProvisionPhaseKey = "state"
27+
InstanceSizeNameKey = "instanceSizeName"
28+
ClusterNameKey = "clusterName"
29+
IPAccessListKey = "ipAccessList"
3030
ConnectionStringsStandardSrvKey = "connectionStringsStandardSrv"
3131
InstanceIDKey = "instanceID"
3232
HostKey = "host"

pkg/api/dbaas/mongodbatlasinstance_types.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ limitations under the License.
1414
package dbaas
1515

1616
import (
17+
"encoding/json"
18+
"io/ioutil"
19+
"net/http"
20+
1721
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1822

1923
dbaasv1alpha1 "github.com/RHEcosystemAppEng/dbaas-operator/api/v1alpha1"
@@ -94,3 +98,27 @@ func GetInstanceCondition(inv *MongoDBAtlasInstance, condType string) *metav1.Co
9498
}
9599
return nil
96100
}
101+
102+
type IP struct {
103+
Query string
104+
}
105+
106+
// GetPublicIP returns the static outbound public IP of the OpenShift Cluster
107+
// Or when the operator runs locally, the h
108+
func GetPublicIP() (string, error) {
109+
req, err := http.Get("http://ip-api.com/json/")
110+
if err != nil {
111+
return "", err
112+
}
113+
defer req.Body.Close()
114+
body, err := ioutil.ReadAll(req.Body)
115+
if err != nil {
116+
return "", err
117+
}
118+
var ip IP
119+
err = json.Unmarshal(body, &ip)
120+
if err != nil {
121+
return "", err
122+
}
123+
return ip.Query, nil
124+
}

pkg/controller/atlasinstance/atlasinstance_controller.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"errors"
2222
"fmt"
23+
"net"
2324
"strings"
2425
"time"
2526

@@ -84,6 +85,7 @@ type InstanceData struct {
8485
ProviderName string
8586
RegionName string
8687
InstanceSizeName string
88+
IPAccessList string
8789
}
8890

8991
const (
@@ -349,6 +351,11 @@ func (r *MongoDBAtlasInstanceReconciler) getAtlasProjectForCreation(instance *db
349351
if err := r.Client.Get(context.Background(), *inventory.ConnectionSecretObjectKey(), secret); err != nil {
350352
return nil, err
351353
}
354+
355+
accessList, err := parseIPAccessList(data.IPAccessList)
356+
if err != nil {
357+
return nil, err
358+
}
352359
return &v1.AtlasProject{
353360
ObjectMeta: metav1.ObjectMeta{
354361
GenerateName: "atlas-project-",
@@ -366,7 +373,7 @@ func (r *MongoDBAtlasInstanceReconciler) getAtlasProjectForCreation(instance *db
366373
Spec: v1.AtlasProjectSpec{
367374
Name: data.ProjectName,
368375
ConnectionSecret: &common.ResourceRef{Name: inventory.Spec.CredentialsRef.Name},
369-
ProjectIPAccessList: []project.IPAccessList{},
376+
ProjectIPAccessList: accessList,
370377
},
371378
}, nil
372379
}
@@ -468,12 +475,24 @@ func getInstanceData(log *zap.SugaredLogger, inst *dbaas.MongoDBAtlasInstance) (
468475
instanceSizeName = "M0"
469476
}
470477

478+
accessIP, ok := inst.Spec.OtherInstanceParams[dbaas.IPAccessListKey]
479+
if !ok || len(strings.TrimSpace(accessIP)) == 0 {
480+
ip, err := dbaas.GetPublicIP()
481+
if err != nil {
482+
log.Infof("Failed to get the public IP")
483+
return nil, err
484+
}
485+
accessIP = ip
486+
log.Infof("%v is missing, current IP %s is used.", dbaas.IPAccessListKey, accessIP)
487+
}
488+
471489
return &InstanceData{
472490
ProjectName: strings.TrimSpace(projectName),
473491
ClusterName: name,
474492
ProviderName: provider,
475493
RegionName: region,
476494
InstanceSizeName: strings.TrimSpace(instanceSizeName),
495+
IPAccessList: strings.TrimSpace(accessIP),
477496
}, nil
478497
}
479498

@@ -521,3 +540,22 @@ func setInstanceStatusWithDeploymentInfo(atlasClient *mongodbatlas.Client, inst
521540
}
522541
return false, result
523542
}
543+
544+
func parseIPAccessList(accessListStr string) ([]project.IPAccessList, error) {
545+
accessList := []project.IPAccessList{}
546+
ranges := strings.Split(accessListStr, " ")
547+
for _, r := range ranges {
548+
ip := net.ParseIP(r)
549+
if ip != nil {
550+
accessList = append(accessList, project.IPAccessList{IPAddress: r})
551+
} else {
552+
_, _, err := net.ParseCIDR(r)
553+
if err == nil {
554+
accessList = append(accessList, project.IPAccessList{CIDRBlock: r})
555+
} else {
556+
return nil, fmt.Errorf("parsing error for ip or ip range: %s", r)
557+
}
558+
}
559+
}
560+
return accessList, nil
561+
}

pkg/controller/atlasinstance/atlasinstance_test.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,38 @@ func TestGetInstanceData(t *testing.T) {
5353
providerName string
5454
regionName string
5555
instanceSizeName string
56+
ipAccessList string
5657
expProviderName string
5758
expRegionName string
5859
expInstanceSizeName string
5960
expErrMsg string
61+
expIPAccessList string
6062
}{
6163
"Nominal": {
6264
deploymentName: "myDeployment",
6365
projectName: "myProject",
6466
providerName: "GCP",
6567
regionName: "GCP_REGION",
6668
instanceSizeName: "M10",
69+
ipAccessList: "192.168.0.1",
6770
expProviderName: "GCP",
6871
expRegionName: "GCP_REGION",
6972
expInstanceSizeName: "M10",
7073
expErrMsg: "",
74+
expIPAccessList: "192.168.0.1",
75+
},
76+
"NominalWithCIDR": {
77+
deploymentName: "myDeployment",
78+
projectName: "myProject",
79+
providerName: "GCP",
80+
regionName: "GCP_REGION",
81+
instanceSizeName: "M10",
82+
ipAccessList: "192.168.0.1/24",
83+
expProviderName: "GCP",
84+
expRegionName: "GCP_REGION",
85+
expInstanceSizeName: "M10",
86+
expErrMsg: "",
87+
expIPAccessList: "192.168.0.1/24",
7188
},
7289
"MissingDeploymentName": {
7390
deploymentName: "",
@@ -101,6 +118,7 @@ func TestGetInstanceData(t *testing.T) {
101118
expRegionName: "AWS_REGION",
102119
expInstanceSizeName: "M10",
103120
expErrMsg: "",
121+
expIPAccessList: "52.206.222.245/32",
104122
},
105123
"UseDefaultRegion": {
106124
deploymentName: "myDeployment",
@@ -112,6 +130,7 @@ func TestGetInstanceData(t *testing.T) {
112130
expRegionName: "US_EAST_1",
113131
expInstanceSizeName: "M10",
114132
expErrMsg: "",
133+
expIPAccessList: "0.0.0.0/0",
115134
},
116135
"UseDefaultInstanceSizeName": {
117136
deploymentName: "myDeployment",
@@ -123,6 +142,7 @@ func TestGetInstanceData(t *testing.T) {
123142
expRegionName: "US_EAST_1",
124143
expInstanceSizeName: "M0",
125144
expErrMsg: "",
145+
expIPAccessList: "52.206.222.245/32",
126146
},
127147
}
128148

@@ -148,6 +168,7 @@ func TestGetInstanceData(t *testing.T) {
148168
OtherInstanceParams: map[string]string{
149169
"projectName": tc.projectName,
150170
"instanceSizeName": tc.instanceSizeName,
171+
"ipAccessList": tc.expIPAccessList,
151172
},
152173
},
153174
}
@@ -158,6 +179,7 @@ func TestGetInstanceData(t *testing.T) {
158179
ProviderName: tc.expProviderName,
159180
RegionName: tc.expRegionName,
160181
InstanceSizeName: tc.expInstanceSizeName,
182+
IPAccessList: tc.expIPAccessList,
161183
}
162184
res, err := getInstanceData(log, instance)
163185
if len(tc.expErrMsg) == 0 {
@@ -384,6 +406,7 @@ func TestAtlasInstanceReconcile(t *testing.T) {
384406
tcName := "mytest"
385407
deploymentName := "mydeploymentnew"
386408
projectName := "myproject"
409+
ipAccessList := "52.206.222.245/32"
387410
expectedPhase := dbaasv1alpha1.InstancePhasePending
388411
expectedErrString := "CLUSTER_NOT_FOUND"
389412
expectedRequeue := true
@@ -436,7 +459,8 @@ func TestAtlasInstanceReconcile(t *testing.T) {
436459
Namespace: inventory.Namespace,
437460
},
438461
OtherInstanceParams: map[string]string{
439-
"projectName": projectName,
462+
"projectName": projectName,
463+
"ipAccessList": ipAccessList,
440464
},
441465
},
442466
}
@@ -470,11 +494,18 @@ func TestAtlasInstanceReconcile(t *testing.T) {
470494
assert.NoError(t, err)
471495
assert.Equal(t, expectedPhase, instanceUpdated.Status.Phase)
472496

497+
// Verify that the AtlasProject created has ipAccessList set
498+
atlasProject, err := r.getAtlasProject(context.Background(), instance)
499+
assert.NoError(t, err)
500+
assert.NotNil(t, atlasProject)
501+
assert.NotEmpty(t, atlasProject.Spec.ProjectIPAccessList)
502+
assert.Equal(t, atlasProject.Spec.ProjectIPAccessList[0].CIDRBlock, ipAccessList)
503+
473504
// After an instance is deleted, the corresponding atlas project should be deleted
474505
delEvent := event.DeleteEvent{Object: instance}
475506
err = r.Delete(delEvent)
476507
assert.NoError(t, err)
477-
atlasProject, err := r.getAtlasProject(context.Background(), instance)
508+
atlasProject, err = r.getAtlasProject(context.Background(), instance)
478509
assert.NoError(t, err)
479510
assert.Nil(t, atlasProject)
480511
}

pkg/controller/dbaasprovider/dbaasprovider_reconciler.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ import (
4444
"sigs.k8s.io/controller-runtime/pkg/controller"
4545
"sigs.k8s.io/controller-runtime/pkg/event"
4646
"sigs.k8s.io/controller-runtime/pkg/predicate"
47+
48+
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/api/dbaas"
4749
)
4850

4951
const (
@@ -164,6 +166,17 @@ func (r *DBaaSProviderReconciler) getAtlasProviderCR(clusterRoleList *rbac.Clust
164166
if err != nil {
165167
return nil, err
166168
}
169+
170+
for ind, spec := range instance.Spec.InstanceParameterSpecs {
171+
if spec.Name == dbaas.IPAccessListKey {
172+
ip, err := dbaas.GetPublicIP()
173+
if err != nil {
174+
return nil, err
175+
}
176+
spec.DefaultValue = ip
177+
instance.Spec.InstanceParameterSpecs[ind] = spec
178+
}
179+
}
167180
instance.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
168181
{
169182
APIVersion: "rbac.authorization.k8s.io/v1",

0 commit comments

Comments
 (0)