Skip to content

Commit

Permalink
Add --config-scope-experimental flag (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
gnossen authored Sep 24, 2021
1 parent 8765051 commit ea8146f
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 3 deletions.
35 changes: 32 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"net/url"
"os"
"path"
"regexp"
"strconv"
"time"

Expand All @@ -46,6 +47,7 @@ var (
gkePodName = flag.String("gke-pod-name-experimental", "", "GKE pod name to use, instead of reading it from $HOSTNAME or /etc/hostname file. This flag is EXPERIMENTAL and may be changed or removed in a later release.")
gkeNamespace = flag.String("gke-namespace-experimental", "", "GKE namespace to use. This flag is EXPERIMENTAL and may be changed or removed in a later release.")
gceVM = flag.String("gce-vm-experimental", "", "GCE VM name to use, instead of reading it from the metadata server. This flag is EXPERIMENTAL and may be changed or removed in a later release.")
configScope = flag.String("config-scope-experimental", "", "Scope dictating which application networking configuration to use. This flag is EXPERIMENTAL and may be changed or removed in a later release.")
)

func main() {
Expand Down Expand Up @@ -111,7 +113,7 @@ func main() {
}
}

config, err := generate(configInput{
input := configInput{
xdsServerUri: *xdsServerUri,
gcpProjectNumber: *gcpProjectNumber,
vpcNetworkName: *vpcNetworkName,
Expand All @@ -122,7 +124,15 @@ func main() {
secretsDir: *secretsDir,
metadataLabels: nodeMetadata,
deploymentInfo: deploymentInfo,
})
configScope: *configScope,
}

if err := validate(input); err != nil {
fmt.Fprintf(os.Stderr, "Error: %s\n", err)
os.Exit(1)
}

config, err := generate(input)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to generate config: %s\n", err)
os.Exit(1)
Expand Down Expand Up @@ -165,6 +175,16 @@ type configInput struct {
secretsDir string
metadataLabels map[string]string
deploymentInfo map[string]string
configScope string
}

func validate(in configInput) error {
re := regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9-]{0,63}$`)
if in.configScope != "" && !re.MatchString(in.configScope) {
return fmt.Errorf("config-scope may only contain letters, numbers, and '-'. It must begin with a letter and must not exceed 64 characters in length")
}

return nil
}

func generate(in configInput) ([]byte, error) {
Expand All @@ -190,14 +210,23 @@ func generate(in configInput) ([]byte, error) {
},
}

if in.configScope != "" {
c.Node.Metadata["TRAFFICDIRECTOR_SCOPE_NAME"] = in.configScope
}

for k, v := range in.metadataLabels {
c.Node.Metadata[k] = v
}
if in.includeV3Features {
// xDS v2 implementation in TD expects the projectNumber and networkName in
// the metadata field while the v3 implementation expects these in the id
// field.
c.Node.Id = fmt.Sprintf("projects/%d/networks/%s/nodes/%s", in.gcpProjectNumber, in.vpcNetworkName, uuid.New().String())
networkIdentifier := in.vpcNetworkName
if in.configScope != "" {
networkIdentifier = fmt.Sprintf("scope:%s", in.configScope)
}

c.Node.Id = fmt.Sprintf("projects/%d/networks/%s/nodes/%s", in.gcpProjectNumber, networkIdentifier, uuid.New().String())
// xDS v2 implementation in TD expects the IP address to be encoded in the
// id field while the v3 implementation expects this in the metadata.
c.Node.Metadata["INSTANCE_IP"] = in.ip
Expand Down
153 changes: 153 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,73 @@ import (
"net"
"net/http"
"net/http/httptest"
"strings"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/uuid"
)

func TestValidate(t *testing.T) {
tests := []struct {
desc string
input configInput
wantError string
}{
{
desc: "fails when config-scope has too many characters",
input: configInput{
xdsServerUri: "example.com:443",
gcpProjectNumber: 123456789012345,
vpcNetworkName: "thedefault",
ip: "10.9.8.7",
zone: "uscentral-5",
metadataLabels: map[string]string{"k1": "v1", "k2": "v2"},
includeV3Features: true,
configScope: strings.Repeat("a", 65),
},
wantError: "config-scope may only contain letters, numbers, and '-'. It must begin with a letter and must not exceed 64 characters in length",
},
{
desc: "fails when config-scope does not start with an alphabetic letter",
input: configInput{
xdsServerUri: "example.com:443",
gcpProjectNumber: 123456789012345,
vpcNetworkName: "thedefault",
ip: "10.9.8.7",
zone: "uscentral-5",
metadataLabels: map[string]string{"k1": "v1", "k2": "v2"},
includeV3Features: true,
configScope: "4foo",
},
wantError: "config-scope may only contain letters, numbers, and '-'. It must begin with a letter and must not exceed 64 characters in length",
},
{
desc: "fails when config-scope contains characters besides letters, numbers, and hyphens.",
input: configInput{
xdsServerUri: "example.com:443",
gcpProjectNumber: 123456789012345,
vpcNetworkName: "thedefault",
ip: "10.9.8.7",
zone: "uscentral-5",
metadataLabels: map[string]string{"k1": "v1", "k2": "v2"},
includeV3Features: true,
configScope: "h*x8",
},
wantError: "config-scope may only contain letters, numbers, and '-'. It must begin with a letter and must not exceed 64 characters in length",
},
}

for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
err := validate(test.input)
if test.wantError != err.Error() {
t.Fatalf("validate(%+v) returned output does not match expected:\nGot: \"%v\"\nWant: \"%s\"", test.input, err.Error(), test.wantError)
}
})
}
}

func TestGenerate(t *testing.T) {
tests := []struct {
desc string
Expand Down Expand Up @@ -211,6 +272,98 @@ func TestGenerate(t *testing.T) {
"zone": "uscentral-5"
}
}
}`,
},
{
desc: "configScope specified",
input: configInput{
xdsServerUri: "example.com:443",
gcpProjectNumber: 123456789012345,
vpcNetworkName: "thedefault",
ip: "10.9.8.7",
zone: "uscentral-5",
includeV3Features: true,
deploymentInfo: map[string]string{
"GCP-ZONE": "uscentral-5",
"GKE-CLUSTER": "test-gke-cluster",
"GKE-NAMESPACE": "test-gke-namespace",
"GKE-POD": "test-gke-pod",
"INSTANCE-IP": "10.9.8.7",
"GCE-VM": "test-gce-vm",
},
configScope: "testscope",
},
wantOutput: `{
"xds_servers": [
{
"server_uri": "example.com:443",
"channel_creds": [
{
"type": "google_default"
}
],
"server_features": [
"xds_v3"
]
}
],
"node": {
"id": "projects/123456789012345/networks/scope:testscope/nodes/9566c74d-1003-4c4d-bbbb-0407d1e2c649",
"cluster": "cluster",
"metadata": {
"INSTANCE_IP": "10.9.8.7",
"TRAFFICDIRECTOR_GCP_PROJECT_NUMBER": "123456789012345",
"TRAFFICDIRECTOR_NETWORK_NAME": "thedefault",
"TRAFFICDIRECTOR_SCOPE_NAME": "testscope",
"TRAFFIC_DIRECTOR_CLIENT_ENVIRONMENT": {
"GCE-VM": "test-gce-vm",
"GCP-ZONE": "uscentral-5",
"GKE-CLUSTER": "test-gke-cluster",
"GKE-NAMESPACE": "test-gke-namespace",
"GKE-POD": "test-gke-pod",
"INSTANCE-IP": "10.9.8.7"
}
},
"locality": {
"zone": "uscentral-5"
}
}
}`,
},
{
desc: "configScope specified with v2 config",
input: configInput{
xdsServerUri: "example.com:443",
gcpProjectNumber: 123456789012345,
vpcNetworkName: "thedefault",
ip: "10.9.8.7",
zone: "uscentral-5",
includeV3Features: false,
configScope: "testscope",
},
wantOutput: `{
"xds_servers": [
{
"server_uri": "example.com:443",
"channel_creds": [
{
"type": "google_default"
}
]
}
],
"node": {
"id": "52fdfc07-2182-454f-963f-5f0f9a621d72~10.9.8.7",
"cluster": "cluster",
"metadata": {
"TRAFFICDIRECTOR_GCP_PROJECT_NUMBER": "123456789012345",
"TRAFFICDIRECTOR_NETWORK_NAME": "thedefault",
"TRAFFICDIRECTOR_SCOPE_NAME": "testscope"
},
"locality": {
"zone": "uscentral-5"
}
}
}`,
},
}
Expand Down

0 comments on commit ea8146f

Please sign in to comment.