Skip to content

Commit b2f8e20

Browse files
committed
feat: add Rackspace DNS provider test and configuration support
1 parent f1c2a64 commit b2f8e20

File tree

7 files changed

+616
-34
lines changed

7 files changed

+616
-34
lines changed

charts/test.yaml

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
---
2+
# Source: external-dns-rackspace/templates/serviceaccount.yaml
3+
# templates/serviceaccount.yaml
4+
apiVersion: v1
5+
kind: ServiceAccount
6+
metadata:
7+
name: external-dns
8+
namespace: default
9+
labels:
10+
app.kubernetes.io/name: external-dns
11+
app.kubernetes.io/instance: dns
12+
---
13+
# Source: external-dns-rackspace/templates/secret.yaml
14+
# templates/secret.yaml
15+
apiVersion: v1
16+
kind: Secret
17+
metadata:
18+
name: external-dns-rackspace-webhook
19+
namespace: default
20+
labels:
21+
app.kubernetes.io/name: external-dns
22+
app.kubernetes.io/instance: dns
23+
type: Opaque
24+
stringData:
25+
username: "YOUR_USERNAME"
26+
api-key: "YOUR_API_KEY"
27+
---
28+
# Source: external-dns-rackspace/templates/clusterrole.yaml
29+
# templates/clusterrole.yaml
30+
apiVersion: rbac.authorization.k8s.io/v1
31+
kind: ClusterRole
32+
metadata:
33+
name: dns-external-dns
34+
labels:
35+
app.kubernetes.io/name: external-dns
36+
app.kubernetes.io/instance: dns
37+
rules:
38+
- apiGroups: [""]
39+
resources: ["services","endpoints","pods", "nodes"]
40+
verbs: ["get","list","watch"]
41+
- apiGroups: ["networking.k8s.io"]
42+
resources: ["ingresses"]
43+
verbs: ["get","list","watch"]
44+
- apiGroups: ["discovery.k8s.io"]
45+
resources: ["endpointslices"]
46+
verbs: ["get","list","watch"]
47+
---
48+
# Source: external-dns-rackspace/templates/clusterrolebinding.yaml
49+
# templates/clusterrolebinding.yaml
50+
apiVersion: rbac.authorization.k8s.io/v1
51+
kind: ClusterRoleBinding
52+
metadata:
53+
name: dns-external-dns-viewer
54+
labels:
55+
app.kubernetes.io/name: external-dns
56+
app.kubernetes.io/instance: dns
57+
roleRef:
58+
apiGroup: rbac.authorization.k8s.io
59+
kind: ClusterRole
60+
name: dns-external-dns
61+
subjects:
62+
- kind: ServiceAccount
63+
name: external-dns
64+
namespace: default
65+
---
66+
# Source: external-dns-rackspace/templates/service.yaml
67+
# templates/service.yaml
68+
apiVersion: v1
69+
kind: Service
70+
metadata:
71+
name: dns-external-dns
72+
namespace: default
73+
labels:
74+
app.kubernetes.io/name: external-dns
75+
app.kubernetes.io/instance: dns
76+
spec:
77+
type: ClusterIP
78+
selector:
79+
app.kubernetes.io/name: external-dns
80+
app.kubernetes.io/instance: dns
81+
ports:
82+
- name: http
83+
port: 7979
84+
targetPort: http
85+
protocol: TCP
86+
---
87+
# Source: external-dns-rackspace/templates/deployment.yaml
88+
# templates/deployment.yaml
89+
apiVersion: apps/v1
90+
kind: Deployment
91+
metadata:
92+
name: dns-external-dns
93+
namespace: default
94+
labels:
95+
app.kubernetes.io/name: external-dns
96+
app.kubernetes.io/instance: dns
97+
spec:
98+
replicas: 1
99+
selector:
100+
matchLabels:
101+
app.kubernetes.io/name: external-dns
102+
app.kubernetes.io/instance: dns
103+
strategy:
104+
type: Recreate
105+
template:
106+
metadata:
107+
labels:
108+
app.kubernetes.io/name: external-dns
109+
app.kubernetes.io/instance: dns
110+
spec:
111+
serviceAccountName: external-dns
112+
securityContext:
113+
fsGroup: 65534
114+
containers:
115+
- name: external-dns
116+
securityContext:
117+
capabilities:
118+
drop:
119+
- ALL
120+
readOnlyRootFilesystem: true
121+
runAsNonRoot: true
122+
runAsUser: 65534
123+
image: "registry.k8s.io/external-dns/external-dns:v0.18.0"
124+
imagePullPolicy: IfNotPresent
125+
args:
126+
- --domain-filter=mydomain.com
127+
- "--source=service"
128+
- "--source=ingress"
129+
- "--interval=2m"
130+
- "--provider=webhook"
131+
- "--webhook-provider-url=http://localhost:8888"
132+
- "--policy=sync"
133+
- "--log-level=debug"
134+
- "--registry=noop"
135+
ports:
136+
- name: http
137+
protocol: TCP
138+
containerPort: 7979
139+
livenessProbe:
140+
failureThreshold: 2
141+
httpGet:
142+
path: /healthz
143+
port: http
144+
initialDelaySeconds: 10
145+
periodSeconds: 10
146+
successThreshold: 1
147+
timeoutSeconds: 5
148+
readinessProbe:
149+
failureThreshold: 6
150+
httpGet:
151+
path: /healthz
152+
port: http
153+
initialDelaySeconds: 5
154+
periodSeconds: 10
155+
successThreshold: 1
156+
timeoutSeconds: 5
157+
resources:
158+
{}
159+
- name: rackspace-webhook
160+
securityContext:
161+
capabilities:
162+
drop:
163+
- ALL
164+
readOnlyRootFilesystem: true
165+
runAsNonRoot: true
166+
runAsUser: 65534
167+
image: "ghcr.io/rackerlabs/external-dns-rackspace-webhook:latest"
168+
imagePullPolicy: IfNotPresent
169+
ports:
170+
- name: http
171+
protocol: TCP
172+
containerPort: 8888
173+
livenessProbe:
174+
failureThreshold: 2
175+
httpGet:
176+
path: /healthz
177+
port: http
178+
initialDelaySeconds: 10
179+
periodSeconds: 10
180+
successThreshold: 1
181+
timeoutSeconds: 5
182+
readinessProbe:
183+
failureThreshold: 6
184+
httpGet:
185+
path: /healthz
186+
port: http
187+
initialDelaySeconds: 5
188+
periodSeconds: 10
189+
successThreshold: 1
190+
timeoutSeconds: 5
191+
env:
192+
- name: RACKSPACE_IDENTITY_ENDPOINT
193+
value: "mydomain.com"
194+
- name: DOMAIN_FILTER
195+
value: "mydomain.com"
196+
- name: RACKSPACE_USERNAME
197+
valueFrom:
198+
secretKeyRef:
199+
name: external-dns-rackspace-webhook
200+
key: username
201+
- name: RACKSPACE_API_KEY
202+
valueFrom:
203+
secretKeyRef:
204+
name: external-dns-rackspace-webhook
205+
key: api-key
206+
- name: DRY_RUN
207+
value: "false"
208+
- name: LOG_LEVEL
209+
value: "info"
210+
- name: RACKSPACE_IDENTITY_ENDPOINT
211+
value: "https://identity.api.rackspacecloud.com/v2.0/"
212+
resources:
213+
{}

charts/values.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ rackspaceWebhook:
7373
env:
7474
LOG_LEVEL: info
7575
DRY_RUN: "false"
76+
RACKSPACE_IDENTITY_ENDPOINT: "https://identity.api.rackspacecloud.com/v2.0/"
7677
containerSecurityContext:
7778
capabilities:
7879
drop:

cmd/webhook/main.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ import (
1414
"github.com/rackerlabs/external-dns-rackspace-webhook/internal/routes"
1515
)
1616

17-
const defaultPort = 8888
17+
const (
18+
defaultPort = 8888
19+
defaultIdentityEndpoint = "https://identity.api.rackspacecloud.com/v2.0/"
20+
)
1821

1922
func main() {
2023
config := loadConfig()
@@ -28,28 +31,33 @@ func main() {
2831
e.HideBanner = true
2932
routes.ConfigureRoutes(e, handler)
3033

31-
if err = e.Start(fmt.Sprintf(":%d", getStartPort())); err != nil {
34+
port, err := getStartPort()
35+
if err != nil {
36+
log.Fatalf("invalid port", err)
37+
}
38+
39+
if err = e.Start(fmt.Sprintf(":%d", port)); err != nil {
3240
log.Fatalf("failed to start server: %v", err)
3341
}
3442
}
3543

36-
func getStartPort() int {
44+
func getStartPort() (int, error) {
3745
portStr := os.Getenv("PORT")
3846
if portStr == "" {
39-
return defaultPort
47+
return defaultPort, nil
4048
}
4149
port, err := strconv.Atoi(portStr)
42-
if err != nil || port <= 0 {
43-
return defaultPort
50+
if err != nil {
51+
return 0, fmt.Errorf("invalid port %s", err.Error())
4452
}
45-
return port
53+
return port, nil
4654
}
4755

4856
func loadConfig() *providers.RackspaceConfig {
4957
config := &providers.RackspaceConfig{
5058
Username: os.Getenv("RACKSPACE_USERNAME"),
5159
APIKey: os.Getenv("RACKSPACE_API_KEY"),
52-
IdentityEndpoint: "https://identity.api.rackspacecloud.com/v2.0/",
60+
IdentityEndpoint: os.Getenv("RACKSPACE_IDENTITY_ENDPOINT"),
5361
DryRun: false,
5462
LogLevel: "info",
5563
}
@@ -66,6 +74,10 @@ func loadConfig() *providers.RackspaceConfig {
6674
config.LogLevel = logLevel
6775
}
6876

77+
if config.IdentityEndpoint == "" {
78+
config.IdentityEndpoint = defaultIdentityEndpoint
79+
}
80+
6981
// Validate required fields
7082
if config.Username == "" || config.APIKey == "" {
7183
log.Fatal("RACKSPACE_USERNAME and RACKSPACE_API_KEY are required")

internal/providers/auth.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package providers
2+
3+
import (
4+
"context"
5+
6+
"github.com/gophercloud/gophercloud/v2"
7+
"github.com/rackerlabs/goclouddns"
8+
"github.com/rackerlabs/goraxauth"
9+
)
10+
11+
// AuthProvider interface abstracts the authentication process
12+
type AuthProvider interface {
13+
Authenticate(ctx context.Context, opts goraxauth.AuthOptions) (*gophercloud.ProviderClient, error)
14+
CreateDNSClient(provider *gophercloud.ProviderClient, opts gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error)
15+
}
16+
17+
type RackspaceAuthProvider struct{}
18+
19+
func NewRackspaceAuthProvider() *RackspaceAuthProvider {
20+
return &RackspaceAuthProvider{}
21+
}
22+
23+
func (r *RackspaceAuthProvider) Authenticate(ctx context.Context, opts goraxauth.AuthOptions) (*gophercloud.ProviderClient, error) {
24+
return goraxauth.AuthenticatedClient(ctx, opts)
25+
}
26+
27+
func (r *RackspaceAuthProvider) CreateDNSClient(provider *gophercloud.ProviderClient, opts gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
28+
return goclouddns.NewCloudDNS(provider, opts)
29+
}

0 commit comments

Comments
 (0)