Skip to content

Commit a5a0e06

Browse files
authored
Merge pull request docker-archive#455 from docker/azure-token
Azure token metaserver
2 parents 4bcf0b4 + 20e0506 commit a5a0e06

File tree

8 files changed

+283
-99
lines changed

8 files changed

+283
-99
lines changed

tools/metaserver/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ RUN go get github.com/docker/docker/api/types
66
RUN go get github.com/docker/go-connections/tlsconfig
77
RUN go get github.com/docker/go-connections/sockets
88
RUN go get github.com/gorilla/mux
9+
RUN go get -u github.com/rancher/trash
910

1011
ENV USER root
1112
WORKDIR /go/

tools/metaserver/compile.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
set -e
44

55
cd /go/src/metaserver
6+
trash
67
go vet
78
go build
89
cp metaserver /go/bin

tools/metaserver/src/metaserver/aws.go

Lines changed: 18 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,24 @@ import (
44
"fmt"
55
"net/http"
66
"os"
7-
"strings"
87

98
"github.com/aws/aws-sdk-go/aws"
109
"github.com/aws/aws-sdk-go/aws/session"
1110
"github.com/aws/aws-sdk-go/service/ec2"
1211
)
1312

14-
type awsInstance struct {
15-
InstanceID string
16-
InstanceType string
17-
PublicIPAddress string
18-
PrivateIPAddress string
19-
InstanceState string
20-
InstanceAZ string
21-
}
22-
13+
// AWSWeb interface for all Azure calls
2314
type AWSWeb struct {
2415
}
2516

17+
// TokenManager obtain the AWS Swarm Manager Token
2618
func (a AWSWeb) TokenManager(w http.ResponseWriter, r *http.Request) {
2719
// get the swarm manager token, if they are a manager node,
2820
// and are not already in the swarm. Block otherwise
2921
RequestInfo(r)
3022
ip := RequestIP(r)
3123
inSwarm := alreadyInSwarm(ip)
32-
isManager := isManagerNode(ip)
24+
isManager := isManagerNode(a, ip)
3325

3426
if inSwarm || !isManager {
3527
// they are either already in the swarm, or they are not a manager
@@ -50,14 +42,15 @@ func (a AWSWeb) TokenManager(w http.ResponseWriter, r *http.Request) {
5042
fmt.Fprintf(w, swarm.JoinTokens.Manager)
5143
}
5244

45+
// TokenWorker obtain the AWS Swarm Worker Token
5346
func (a AWSWeb) TokenWorker(w http.ResponseWriter, r *http.Request) {
5447
// get the swarm worker token, if they are a worker node,
5548
// and are not already in the swarm. block otherwise
5649
RequestInfo(r)
5750

5851
ip := RequestIP(r)
5952
inSwarm := alreadyInSwarm(ip)
60-
isWorker := isWorkerNode(ip)
53+
isWorker := isWorkerNode(a, ip)
6154

6255
if inSwarm || !isWorker {
6356
// they are either already in the swarm, or they are not a worker
@@ -78,75 +71,45 @@ func (a AWSWeb) TokenWorker(w http.ResponseWriter, r *http.Request) {
7871
fmt.Fprintf(w, swarm.JoinTokens.Worker)
7972
}
8073

81-
func alreadyInSwarm(ip string) bool {
82-
// Is the node making the request, already in the swarm.
83-
nodes := SwarmNodes()
84-
for _, node := range nodes {
85-
nodeIP := ConvertAWSHostToIP(node.Description.Hostname)
86-
if ip == nodeIP {
87-
return true
88-
}
89-
}
90-
return false
91-
}
92-
93-
func isManagerNode(ip string) bool {
94-
// Is the node making the request a manager node
95-
return isNodeInList(ip, awsManagers())
96-
}
97-
98-
func isWorkerNode(ip string) bool {
99-
// Is the node making the request a worker node
100-
return isNodeInList(ip, awsWorkers())
101-
}
102-
103-
func isNodeInList(ip string, instances []awsInstance) bool {
104-
// given an IP, find out if it is in the instance list.
105-
for _, instance := range instances {
106-
if ip == instance.PrivateIPAddress {
107-
return true
108-
}
109-
}
110-
return false
111-
}
112-
113-
func awsWorkers() []awsInstance {
114-
// get the instances from AWS worker security group
74+
// Managers get list of AWS manager instances
75+
func (a AWSWeb) Managers() []WebInstance {
76+
// get the instances from AWS Manager security group
11577
customFilter := []*ec2.Filter{
11678
&ec2.Filter{
11779
Name: aws.String("tag:swarm-node-type"),
118-
Values: []*string{aws.String("worker")},
80+
Values: []*string{aws.String("manager")},
11981
},
12082
}
12183

12284
// ec2 classic would be just group-id, VPC would be instance.group-id
12385
customFilter = append(customFilter, &ec2.Filter{
12486
Name: aws.String("instance.group-id"),
125-
Values: []*string{aws.String(os.Getenv("WORKER_SECURITY_GROUP_ID"))},
87+
Values: []*string{aws.String(os.Getenv("MANAGER_SECURITY_GROUP_ID"))},
12688
})
12789

12890
return awsInstances(customFilter)
12991
}
13092

131-
func awsManagers() []awsInstance {
132-
// get the instances from AWS Manager security group
93+
// Workers get list of AWS worker instances
94+
func (a AWSWeb) Workers() []WebInstance {
95+
// get the instances from AWS worker security group
13396
customFilter := []*ec2.Filter{
13497
&ec2.Filter{
13598
Name: aws.String("tag:swarm-node-type"),
136-
Values: []*string{aws.String("manager")},
99+
Values: []*string{aws.String("worker")},
137100
},
138101
}
139102

140103
// ec2 classic would be just group-id, VPC would be instance.group-id
141104
customFilter = append(customFilter, &ec2.Filter{
142105
Name: aws.String("instance.group-id"),
143-
Values: []*string{aws.String(os.Getenv("MANAGER_SECURITY_GROUP_ID"))},
106+
Values: []*string{aws.String(os.Getenv("WORKER_SECURITY_GROUP_ID"))},
144107
})
145108

146109
return awsInstances(customFilter)
147110
}
148111

149-
func awsInstances(customFilters []*ec2.Filter) []awsInstance {
112+
func awsInstances(customFilters []*ec2.Filter) []WebInstance {
150113
// get the instances from AWS, takes a filter to limit the results.
151114
client := ec2.New(session.New(&aws.Config{}))
152115

@@ -173,10 +136,10 @@ func awsInstances(customFilters []*ec2.Filter) []awsInstance {
173136
fmt.Println(err.Error())
174137
}
175138

176-
var instances []awsInstance
139+
var instances []WebInstance
177140
for _, reservation := range result.Reservations {
178141
for _, instance := range reservation.Instances {
179-
aInstance := awsInstance{
142+
aInstance := WebInstance{
180143
InstanceID: *instance.InstanceId,
181144
InstanceType: *instance.InstanceType,
182145
PublicIPAddress: *instance.PublicIpAddress,
@@ -189,18 +152,3 @@ func awsInstances(customFilters []*ec2.Filter) []awsInstance {
189152
}
190153
return instances
191154
}
192-
193-
func ConvertAWSHostToIP(hostStr string) string {
194-
// This is risky, this assumes the following formation for hosts in swarm node ls
195-
// ip-10-0-3-149.ec2.internal
196-
// there was one use case when someone had an old account, and their hostnames were not
197-
// in this format. they just had
198-
// ip-192-168-33-67
199-
// not sure how many other formats there are.
200-
// This will work for both formats above.
201-
hostSplit := strings.Split(hostStr, ".")
202-
host := hostSplit[0]
203-
host = strings.Replace(host, "ip-", "", -1)
204-
ip := strings.Replace(host, "-", ".", -1)
205-
return ip
206-
}

tools/metaserver/src/metaserver/aws_test.go

Lines changed: 0 additions & 12 deletions
This file was deleted.

tools/metaserver/src/metaserver/azure.go

Lines changed: 168 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,187 @@ package main
33
import (
44
"fmt"
55
"net/http"
6+
"os"
7+
8+
"github.com/Azure/azure-sdk-for-go/arm/compute"
9+
"github.com/Azure/azure-sdk-for-go/arm/examples/helpers"
10+
"github.com/Azure/azure-sdk-for-go/arm/network"
11+
"github.com/Azure/go-autorest/autorest/azure"
612
)
713

14+
// AzureWeb interface for all Azure calls
815
type AzureWeb struct {
916
}
1017

11-
//TODO: implement these using Azure specific API's
12-
18+
// TokenManager obtain the Azure Swarm Manager Token
1319
func (a AzureWeb) TokenManager(w http.ResponseWriter, r *http.Request) {
1420
RequestInfo(r)
21+
ip := RequestIP(r)
22+
inSwarm := alreadyInSwarm(ip)
23+
isManager := isManagerNode(a, ip)
24+
25+
if inSwarm || !isManager {
26+
// they are either already in the swarm, or they are not a manager
27+
w.WriteHeader(http.StatusForbidden)
28+
fmt.Fprintln(w, "Access Denied")
29+
return
30+
}
1531

16-
w.WriteHeader(http.StatusNotImplemented)
17-
fmt.Fprintf(w, "Not implmented Yet")
32+
// They are not in the swarm, and they are a manager, so good to go.
33+
cli, ctx := DockerClient()
34+
swarm, err := cli.SwarmInspect(ctx)
35+
if err != nil {
36+
w.WriteHeader(http.StatusInternalServerError)
37+
fmt.Fprintf(w, "%v", err)
38+
return
39+
}
1840

19-
fmt.Println("Endpoint Hit: tokenManager")
41+
fmt.Fprintf(w, swarm.JoinTokens.Manager)
2042
}
2143

44+
// TokenWorker obtain the Azure Swarm Worker Token
2245
func (a AzureWeb) TokenWorker(w http.ResponseWriter, r *http.Request) {
46+
// get the swarm worker token, if they are a worker node,
47+
// and are not already in the swarm. block otherwise
2348
RequestInfo(r)
2449

25-
w.WriteHeader(http.StatusNotImplemented)
26-
fmt.Fprintf(w, "Not implmented Yet")
50+
ip := RequestIP(r)
51+
inSwarm := alreadyInSwarm(ip)
52+
isWorker := isWorkerNode(a, ip)
53+
54+
if inSwarm || !isWorker {
55+
// they are either already in the swarm, or they are not a worker
56+
w.WriteHeader(http.StatusForbidden)
57+
fmt.Fprintln(w, "Access Denied")
58+
return
59+
}
60+
61+
// They are not in the swarm, and they are a worker, so good to go.
62+
cli, ctx := DockerClient()
63+
swarm, err := cli.SwarmInspect(ctx)
64+
if err != nil {
65+
w.WriteHeader(http.StatusInternalServerError)
66+
fmt.Fprintf(w, "%v", err)
67+
return
68+
}
69+
70+
fmt.Fprintf(w, swarm.JoinTokens.Worker)
71+
}
72+
73+
// Managers get list of Azure manager instances
74+
func (a AzureWeb) Managers() []WebInstance {
75+
// get the clients for Network and Compute
76+
env := map[string]string{
77+
"AZURE_CLIENT_ID": os.Getenv("APP_ID"),
78+
"AZURE_CLIENT_SECRET": os.Getenv("APP_SECRET"),
79+
"AZURE_SUBSCRIPTION_ID": os.Getenv("SUBSCRIPTION_ID"),
80+
"AZURE_TENANT_ID": os.Getenv("TENANT_ID"),
81+
"AZURE_GROUP_NAME": os.Getenv("GROUP_NAME"),
82+
"AZURE_VMSS_MGR": os.Getenv("VMSS_MGR"),
83+
"AZURE_VMSS_WRK": os.Getenv("VMSS_WRK")}
84+
nicClient, vmssClient := initClients(env)
85+
// Get list of VMSS Network Interfaces for Managers
86+
managerIPTable, err := getVMSSNic(nicClient, env, env["AZURE_VMSS_MGR"])
87+
if err != nil {
88+
fmt.Printf("Couldn't get Manager Nic for VMSS: %v", err)
89+
return []WebInstance{}
90+
}
91+
// Get list of VMSS for Managers
92+
managerVMs, err := getVMSSList(vmssClient, env, env["AZURE_VMSS_MGR"], managerIPTable)
93+
if err != nil {
94+
fmt.Printf("Couldn't get List of Manager VMSS: %v", err)
95+
return []WebInstance{}
96+
}
97+
return managerVMs
98+
}
99+
100+
// Workers get list of Azure worker instances
101+
func (a AzureWeb) Workers() []WebInstance {
102+
// get the clients for Network and Compute
103+
env := map[string]string{
104+
"AZURE_CLIENT_ID": os.Getenv("APP_ID"),
105+
"AZURE_CLIENT_SECRET": os.Getenv("APP_SECRET"),
106+
"AZURE_SUBSCRIPTION_ID": os.Getenv("SUBSCRIPTION_ID"),
107+
"AZURE_TENANT_ID": os.Getenv("TENANT_ID"),
108+
"AZURE_GROUP_NAME": os.Getenv("GROUP_NAME"),
109+
"AZURE_VMSS_MGR": os.Getenv("VMSS_MGR"),
110+
"AZURE_VMSS_WRK": os.Getenv("VMSS_WRK")}
111+
nicClient, vmssClient := initClients(env)
112+
// Get list of VMSS Network Interfaces for Managers
113+
workerIPTable, err := getVMSSNic(nicClient, env, env["AZURE_VMSS_WRK"])
114+
if err != nil {
115+
fmt.Printf("Couldn't get Worker Nic for VMSS: %v", err)
116+
return []WebInstance{}
117+
}
118+
// Get list of VMSS for Managers
119+
workerVMs, err := getVMSSList(vmssClient, env, env["AZURE_VMSS_WRK"], workerIPTable)
120+
if err != nil {
121+
fmt.Printf("Couldn't get List of Worker VMSS: %v", err)
122+
return []WebInstance{}
123+
}
124+
return workerVMs
125+
}
126+
127+
func initClients(env map[string]string) (network.InterfacesClient, compute.VirtualMachineScaleSetVMsClient) {
128+
129+
spt, err := helpers.NewServicePrincipalTokenFromCredentials(env, azure.PublicCloud.ResourceManagerEndpoint)
130+
if err != nil {
131+
fmt.Printf("ERROR: Getting SP token - check that all ENV variables are set")
132+
os.Exit(1)
133+
}
134+
// Create Network Interface Client
135+
nicClient := network.NewInterfacesClient(env["AZURE_SUBSCRIPTION_ID"])
136+
nicClient.Authorizer = spt
137+
// Create VMSS Client
138+
vmssClient := compute.NewVirtualMachineScaleSetVMsClient(env["AZURE_SUBSCRIPTION_ID"])
139+
vmssClient.Authorizer = spt
140+
return nicClient, vmssClient
141+
}
142+
143+
func getVMSSNic(client network.InterfacesClient, env map[string]string, vmss string) (IPTable map[string]string, err error) {
144+
result, err := client.ListVirtualMachineScaleSetNetworkInterfaces(env["AZURE_GROUP_NAME"], vmss)
145+
if err != nil {
146+
// Message from an error.
147+
fmt.Println("Error: ", err.Error())
148+
return IPTable, err
149+
}
150+
151+
IPTable = map[string]string{}
152+
153+
for _, nic := range *result.Value {
154+
if *nic.Properties.Primary {
155+
for _, ipConfig := range *nic.Properties.IPConfigurations {
156+
if *ipConfig.Properties.Primary {
157+
IPTable[*nic.ID] = *ipConfig.Properties.PrivateIPAddress
158+
}
159+
}
160+
}
161+
}
162+
return IPTable, nil
163+
}
164+
165+
func getVMSSList(client compute.VirtualMachineScaleSetVMsClient, env map[string]string, vmss string, nicIPTable map[string]string) ([]WebInstance, error) {
166+
vms := []WebInstance{}
167+
168+
result, err := client.List(env["AZURE_GROUP_NAME"], vmss, "", "", "")
169+
if err != nil {
170+
// Message from an error.
171+
fmt.Println("Error: ", err.Error())
172+
return vms, err
173+
}
27174

28-
fmt.Println("Endpoint Hit: tokenWorker")
175+
for _, vm := range *result.Value {
176+
nics := *vm.Properties.NetworkProfile.NetworkInterfaces
177+
privateIP := nicIPTable[*nics[0].ID]
178+
newVM := WebInstance{
179+
ID: *vm.ID,
180+
InstanceID: *vm.InstanceID,
181+
InstanceName: *vm.Name,
182+
InstanceType: *vm.Type,
183+
InstanceNic: *nics[0].ID,
184+
PrivateIPAddress: privateIP,
185+
InstanceState: *vm.Properties.ProvisioningState}
186+
vms = append(vms, newVM)
187+
}
188+
return vms, nil
29189
}

0 commit comments

Comments
 (0)