diff --git a/acstgen/README.md b/acstgen/README.md new file mode 100644 index 0000000000..4fa14d986a --- /dev/null +++ b/acstgen/README.md @@ -0,0 +1,5 @@ +# ACSTGEN - Template generator + +Template generator builds a custom template based on user requirements. Examples exist under clusterdefinitions folder. + +To build type ```go build```, and running at commandline provides the usage. diff --git a/acstgen/acstgen.go b/acstgen/acstgen.go new file mode 100644 index 0000000000..db13ed6d14 --- /dev/null +++ b/acstgen/acstgen.go @@ -0,0 +1,139 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "os" + "strings" + + "./api/vlabs" + "./clustertemplate" +) + +// loadAcsCluster loads an ACS Cluster API Model from a JSON file +func loadAcsCluster(jsonFile string) (*vlabs.AcsCluster, error) { + contents, e := ioutil.ReadFile(jsonFile) + if e != nil { + return nil, fmt.Errorf("error reading file %s: %s", jsonFile, e.Error()) + } + + acsCluster := &vlabs.AcsCluster{} + if e := json.Unmarshal(contents, &acsCluster); e != nil { + return nil, fmt.Errorf("error unmarshalling file %s: %s", jsonFile, e.Error()) + } + acsCluster.SetDefaults() + if e := acsCluster.Validate(); e != nil { + return nil, fmt.Errorf("error validating acs cluster from file %s: %s", jsonFile, e.Error()) + } + + return acsCluster, nil +} + +func translateJSON(content string, translateParams [][]string, reverseTranslate bool) string { + for _, tuple := range translateParams { + if len(tuple) != 2 { + panic("string tuples must be of size 2") + } + a := tuple[0] + b := tuple[1] + if reverseTranslate { + content = strings.Replace(content, b, a, -1) + } else { + content = strings.Replace(content, a, b, -1) + } + } + return content +} + +func prettyPrintJSON(content string) (string, error) { + var data map[string]interface{} + if err := json.Unmarshal([]byte(content), &data); err != nil { + return "", err + } + prettyprint, err := json.MarshalIndent(data, "", " ") + if err != nil { + return "", err + } + return string(prettyprint), nil +} + +func prettyPrintArmTemplate(template string) (string, error) { + translateParams := [][]string{ + {"parameters", "dparameters"}, + {"variables", "evariables"}, + {"resources", "fresources"}, + {"outputs", "zoutputs"}, + } + + template = translateJSON(template, translateParams, false) + var err error + if template, err = prettyPrintJSON(template); err != nil { + return "", err + } + template = translateJSON(template, translateParams, true) + + return template, nil +} + +func usage(errs ...error) { + for _, err := range errs { + fmt.Fprintf(os.Stderr, "error: %s\n\n", err.Error()) + } + fmt.Fprintf(os.Stderr, "usage: %s ClusterDefinitionFile\n", os.Args[0]) + fmt.Fprintf(os.Stderr, " read the ClusterDefinitionFile and output an arm template") + fmt.Fprintf(os.Stderr, "\n") + fmt.Fprintf(os.Stderr, "options:\n") + flag.PrintDefaults() +} + +var templateDirectory = flag.String("templateDirectory", "./parts", "directory containing base template files") +var noPrettyPrint = flag.Bool("noPrettyPrint", false, "do not pretty print output") + +func main() { + var acsCluster *vlabs.AcsCluster + var template string + var err error + + flag.Parse() + + if argCount := len(flag.Args()); argCount == 0 { + usage() + os.Exit(1) + } + + jsonFile := flag.Arg(0) + if _, err = os.Stat(jsonFile); os.IsNotExist(err) { + usage(fmt.Errorf("file %s does not exist", jsonFile)) + os.Exit(1) + } + + if _, err = os.Stat(*templateDirectory); os.IsNotExist(err) { + usage(fmt.Errorf("base templates directory %s does not exist", jsonFile)) + os.Exit(1) + } + + if err = clustertemplate.VerifyFiles(*templateDirectory); err != nil { + fmt.Fprintf(os.Stderr, "verification failed: %s\n", err.Error()) + os.Exit(1) + } + + if acsCluster, err = loadAcsCluster(jsonFile); err != nil { + fmt.Fprintf(os.Stderr, "error while loading %s: %s", jsonFile, err.Error()) + os.Exit(1) + } + + if template, err = clustertemplate.GenerateTemplate(acsCluster, *templateDirectory); err != nil { + fmt.Fprintf(os.Stderr, "error generating template %s: %s", jsonFile, err.Error()) + os.Exit(1) + } + + if !*noPrettyPrint { + if template, err = prettyPrintArmTemplate(template); err != nil { + fmt.Fprintf(os.Stderr, "error pretty printing template %s", err.Error()) + os.Exit(1) + } + } + fmt.Print(template) +} diff --git a/acstgen/api/vlabs/const.go b/acstgen/api/vlabs/const.go new file mode 100644 index 0000000000..354c99da24 --- /dev/null +++ b/acstgen/api/vlabs/const.go @@ -0,0 +1,30 @@ +package vlabs + +const ( + // DCOS is the string constant for DCOS orchestrator type and defaults to DCOS184 + DCOS = "DCOS" + // DCOS184 is the string constant for DCOS 1.8.4 orchestrator type + DCOS184 = "DCOS184" + // DCOS173 is the string constant for DCOS 1.7.3 orchestrator type + DCOS173 = "DCOS173" + // Swarm is the string constant for the Swarm orchestrator type + Swarm = "Swarm" + // MinAgentCount are the minimum number of agents + MinAgentCount = 1 + // MaxAgentCount are the maximum number of agents + MaxAgentCount = 100 + // MinPort specifies the minimum tcp port to open + MinPort = 1 + // MaxPort specifies the maximum tcp port to open + MaxPort = 65535 + // BaseLBPriority specifies the base lb priority. + BaseLBPriority = 200 + // DefaultMasterSubnet specifies the default master subnet + DefaultMasterSubnet = "172.16.0.0/24" + // DefaultFirstConsecutiveStaticIP specifies the static IP address on master 0 + DefaultFirstConsecutiveStaticIP = "172.16.0.5" + // DefaultAgentSubnetTemplate specifies a default agent subnet + DefaultAgentSubnetTemplate = "10.%d.0.0/24" + // MaxDisks specifies the maximum attached disks to add to the cluster + MaxDisks = 4 +) diff --git a/acstgen/api/vlabs/defaults.go b/acstgen/api/vlabs/defaults.go new file mode 100644 index 0000000000..4e7bedb45c --- /dev/null +++ b/acstgen/api/vlabs/defaults.go @@ -0,0 +1,41 @@ +package vlabs + +import "fmt" + +// SetDefaults implements APIObject +func (o *OrchestratorProfile) SetDefaults() { +} + +// SetDefaults implements APIObject +func (m *MasterProfile) SetDefaults() { + if !m.IsCustomVNET() { + m.subnet = DefaultMasterSubnet + m.FirstConsecutiveStaticIP = DefaultFirstConsecutiveStaticIP + } +} + +// SetDefaults implements APIObject +func (a *AgentPoolProfile) SetDefaults() { +} + +// SetDefaults implements APIObject +func (l *LinuxProfile) SetDefaults() { +} + +// SetDefaults implements APIObject +func (a *AcsCluster) SetDefaults() { + a.OrchestratorProfile.SetDefaults() + a.MasterProfile.SetDefaults() + + // assign subnets if VNET not specified + subnetCounter := 0 + for i := range a.AgentPoolProfiles { + profile := &a.AgentPoolProfiles[i] + profile.SetDefaults() + if !profile.IsCustomVNET() { + profile.subnet = fmt.Sprintf(DefaultAgentSubnetTemplate, subnetCounter) + subnetCounter++ + } + } + a.LinuxProfile.SetDefaults() +} diff --git a/acstgen/api/vlabs/doc.go b/acstgen/api/vlabs/doc.go new file mode 100644 index 0000000000..5a4c4b8508 --- /dev/null +++ b/acstgen/api/vlabs/doc.go @@ -0,0 +1,2 @@ +// Package vlabs stores an experimental api model for acs +package vlabs // import "./api/vlabs" diff --git a/acstgen/api/vlabs/types.go b/acstgen/api/vlabs/types.go new file mode 100644 index 0000000000..35093ca949 --- /dev/null +++ b/acstgen/api/vlabs/types.go @@ -0,0 +1,80 @@ +package vlabs + +// AcsCluster represents the ACS cluster definition +type AcsCluster struct { + OrchestratorProfile OrchestratorProfile `json:"orchestratorProfile"` + MasterProfile MasterProfile `json:"masterProfile"` + AgentPoolProfiles []AgentPoolProfile `json:"agentPoolProfiles"` + LinuxProfile LinuxProfile `json:"linuxProfile"` +} + +// OrchestratorProfile represents the type of orchestrator +type OrchestratorProfile struct { + OrchestratorType string `json:"orchestratorType"` +} + +// MasterProfile represents the definition of the master cluster +type MasterProfile struct { + Count int `json:"count"` + DNSPrefix string `json:"dnsPrefix"` + VMSize string `json:"vmSize"` + VnetSubnetID string `json:"vnetSubnetID,omitempty"` + FirstConsecutiveStaticIP string `json:"firstConsecutiveStaticIP,omitempty"` + // subnet is internal + subnet string +} + +// AgentPoolProfile represents an agent pool definition +type AgentPoolProfile struct { + Name string `json:"name"` + Count int `json:"count"` + VMSize string `json:"vmSize"` + DNSPrefix string `json:"dnsPrefix,omitempty"` + Ports []int `json:"ports,omitempty"` + IsStateful bool `json:"isStateful,omitempty"` + DiskSizesGB []int `json:"diskSizesGB,omitempty"` + VnetSubnetID string `json:"vnetSubnetID,omitempty"` + // subnet is internal + subnet string +} + +// LinuxProfile represents the linux parameters passed to the cluster +type LinuxProfile struct { + AdminUsername string `json:"adminUsername"` + SSH struct { + PublicKeys []struct { + KeyData string `json:"keyData"` + } `json:"publicKeys"` + } `json:"ssh"` +} + +// APIObject defines the required functionality of an api object +type APIObject interface { + SetDefaults() + Validate() error +} + +// IsCustomVNET returns true if the customer brought their own VNET +func (m *MasterProfile) IsCustomVNET() bool { + return len(m.VnetSubnetID) > 0 +} + +// GetSubnet returns the read-only subnet for the master +func (m *MasterProfile) GetSubnet() string { + return m.subnet +} + +// IsCustomVNET returns true if the customer brought their own VNET +func (a *AgentPoolProfile) IsCustomVNET() bool { + return len(a.VnetSubnetID) > 0 +} + +// HasDisks returns true if the customer specified disks +func (a *AgentPoolProfile) HasDisks() bool { + return len(a.DiskSizesGB) > 0 +} + +// GetSubnet returns the read-only subnet for the agent pool +func (a *AgentPoolProfile) GetSubnet() string { + return a.subnet +} diff --git a/acstgen/api/vlabs/validate.go b/acstgen/api/vlabs/validate.go new file mode 100644 index 0000000000..1143d1359f --- /dev/null +++ b/acstgen/api/vlabs/validate.go @@ -0,0 +1,285 @@ +package vlabs + +import ( + "errors" + "fmt" + "regexp" + "strconv" +) + +// Validate implements APIObject +func (o *OrchestratorProfile) Validate() error { + switch o.OrchestratorType { + case DCOS: + case DCOS184: + case DCOS173: + case Swarm: + default: + return fmt.Errorf("OrchestratorProfile has unknown orchestrator: %s", o.OrchestratorType) + } + return nil +} + +// Validate implements APIObject +func (m *MasterProfile) Validate() error { + if m.Count != 1 && m.Count != 3 && m.Count != 5 { + return fmt.Errorf("MasterProfile count needs to be 1, 3, or 5") + } + if e := validateName(m.DNSPrefix, "MasterProfile.DNSPrefix"); e != nil { + return e + } + if e := validateDNSName(m.DNSPrefix); e != nil { + return e + } + if e := validateName(m.VMSize, "MasterProfile.VMSize"); e != nil { + return e + } + return nil +} + +// Validate implements APIObject +func (a *AgentPoolProfile) Validate() error { + if e := validateName(a.Name, "AgentPoolProfile.Name"); e != nil { + return e + } + if e := validatePoolName(a.Name); e != nil { + return e + } + if a.Count < MinAgentCount || a.Count > MaxAgentCount { + return fmt.Errorf("AgentPoolProfile count needs to be in the range [%d,%d]", MinAgentCount, MaxAgentCount) + } + if e := validateName(a.VMSize, "AgentPoolProfile.VMSize"); e != nil { + return e + } + if len(a.Ports) > 0 { + if e := validateUniquePorts(a.Ports, a.Name); e != nil { + return e + } + for _, port := range a.Ports { + if port < MinPort || port > MaxPort { + return fmt.Errorf("AgentPoolProfile Ports must be in the range[%d, %d]", MinPort, MaxPort) + } + } + if e := validateName(a.DNSPrefix, "AgentPoolProfile.DNSPrefix when specifying AgentPoolProfile Ports"); e != nil { + return e + } + if e := validateDNSName(a.DNSPrefix); e != nil { + return e + } + } + if len(a.DiskSizesGB) > 0 && !a.IsStateful { + return fmt.Errorf("Disks were specified on a non stateful cluster named '%s'. Ensure you add '\"isStateful\": true' to the model", a.Name) + } + if len(a.DiskSizesGB) > MaxDisks { + return fmt.Errorf("A maximum of %d disks may be specified. %d disks were specified for cluster named '%s'", MaxDisks, len(a.DiskSizesGB), a.Name) + } + if len(a.Ports) == 0 && len(a.DNSPrefix) > 0 { + return fmt.Errorf("AgentPoolProfile.Ports must be non empty when AgentPoolProfile.DNSPrefix is specified") + } + return nil +} + +// Validate implements APIObject +func (l *LinuxProfile) Validate() error { + if e := validateName(l.AdminUsername, "LinuxProfile.AdminUsername"); e != nil { + return e + } + if len(l.SSH.PublicKeys) != 1 { + return errors.New("LinuxProfile.PublicKeys requires only 1 SSH Key") + } + if e := validateName(l.SSH.PublicKeys[0].KeyData, "LinuxProfile.PublicKeys.KeyData"); e != nil { + return e + } + return nil +} + +// Validate implements APIObject +func (a *AcsCluster) Validate() error { + if e := a.OrchestratorProfile.Validate(); e != nil { + return e + } + if e := a.MasterProfile.Validate(); e != nil { + return e + } + if e := validateUniqueProfileNames(a.AgentPoolProfiles); e != nil { + return e + } + for _, agentPoolProfile := range a.AgentPoolProfiles { + if e := agentPoolProfile.Validate(); e != nil { + return e + } + if a.OrchestratorProfile.OrchestratorType == Swarm && agentPoolProfile.IsStateful { + return errors.New("stateful deployments are not supported with Swarm, please let us know if you want this feature") + } + } + if e := a.LinuxProfile.Validate(); e != nil { + return e + } + if e := validateVNET(a); e != nil { + return e + } + return nil +} + +func validateName(name string, label string) error { + if name == "" { + return fmt.Errorf("%s must be a non-empty value", label) + } + return nil +} + +func parseCIDR(cidr string) (octet1 int, octet2 int, octet3 int, octet4 int, subnet int, err error) { + // verify cidr format and a /24 subnet + // regular expression inspired by http://blog.markhatton.co.uk/2011/03/15/regular-expressions-for-ip-addresses-cidr-ranges-and-hostnames/ + cidrRegex := `^((?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\.((?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\.((?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\/((?:[0-9]|[1-2][0-9]|3[0-2]))$` + var re *regexp.Regexp + if re, err = regexp.Compile(cidrRegex); err != nil { + return 0, 0, 0, 0, 0, err + } + submatches := re.FindStringSubmatch(cidr) + if len(submatches) != 6 { + return 0, 0, 0, 0, 0, fmt.Errorf("address %s is not specified as valid cidr", cidr) + } + if octet1, err = strconv.Atoi(submatches[1]); err != nil { + return 0, 0, 0, 0, 0, err + } + if octet2, err = strconv.Atoi(submatches[2]); err != nil { + return 0, 0, 0, 0, 0, err + } + if octet3, err = strconv.Atoi(submatches[3]); err != nil { + return 0, 0, 0, 0, 0, err + } + if octet4, err = strconv.Atoi(submatches[4]); err != nil { + return 0, 0, 0, 0, 0, err + } + if subnet, err = strconv.Atoi(submatches[5]); err != nil { + return 0, 0, 0, 0, 0, err + } + return octet1, octet2, octet3, octet4, subnet, nil +} + +func validatePoolName(poolName string) error { + // we will cap at length of 12 and all lowercase letters since this makes up the VMName + poolNameRegex := `^([a-z][a-z0-9]{0,11})$` + re, err := regexp.Compile(poolNameRegex) + if err != nil { + return err + } + submatches := re.FindStringSubmatch(poolName) + if len(submatches) != 2 { + return fmt.Errorf("pool name '%s' is invalid. A pool name must start with a lowercase letter, have max length of 12, and only have characters a-z0-9", poolName) + } + return nil +} + +func validateDNSName(dnsName string) error { + dnsNameRegex := `^([a-z][a-z0-9-]{1,13}[a-z0-9])$` + re, err := regexp.Compile(dnsNameRegex) + if err != nil { + return err + } + submatches := re.FindStringSubmatch(dnsName) + if len(submatches) != 2 { + return fmt.Errorf("DNS name '%s' is invalid. The DNS name must contain between 3 and 15 characters. The name can contain only letters, numbers, and hyphens. The name must start with a letter and must end with a letter or a number", dnsName) + } + return nil +} + +func parseIP(ipaddress string) (octet1 int, octet2 int, octet3 int, octet4 int, err error) { + // verify cidr format and a /24 subnet + // regular expression inspired by http://blog.markhatton.co.uk/2011/03/15/regular-expressions-for-ip-addresses-cidr-ranges-and-hostnames/ + ipRegex := `^((?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\.((?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\.((?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$` + var re *regexp.Regexp + if re, err = regexp.Compile(ipRegex); err != nil { + return 0, 0, 0, 0, err + } + submatches := re.FindStringSubmatch(ipaddress) + if len(submatches) != 5 { + return 0, 0, 0, 0, fmt.Errorf("address %s is not specified as a valid ip address", ipaddress) + } + if octet1, err = strconv.Atoi(submatches[1]); err != nil { + return 0, 0, 0, 0, err + } + if octet2, err = strconv.Atoi(submatches[2]); err != nil { + return 0, 0, 0, 0, err + } + if octet3, err = strconv.Atoi(submatches[3]); err != nil { + return 0, 0, 0, 0, err + } + if octet4, err = strconv.Atoi(submatches[4]); err != nil { + return 0, 0, 0, 0, err + } + return octet1, octet2, octet3, octet4, nil +} + +func validateUniqueProfileNames(profiles []AgentPoolProfile) error { + profileNames := make(map[string]bool) + for _, profile := range profiles { + if _, ok := profileNames[profile.Name]; ok { + return fmt.Errorf("profile name '%s' already exists, profile names must be unique across pools", profile.Name) + } + profileNames[profile.Name] = true + } + return nil +} + +func validateUniquePorts(ports []int, name string) error { + portMap := make(map[int]bool) + for _, port := range ports { + if _, ok := portMap[port]; ok { + return fmt.Errorf("agent profile '%s' has duplicate port '%d', ports must be unique", name, port) + } + portMap[port] = true + } + return nil +} + +func validateVNET(a *AcsCluster) error { + isCustomVNET := a.MasterProfile.IsCustomVNET() + for _, agentPool := range a.AgentPoolProfiles { + if agentPool.IsCustomVNET() != isCustomVNET { + return fmt.Errorf("Multiple VNET Subnet configurations specified. The master profile and each agent pool profile must all specify a custom VNET Subnet, or none at all.") + } + } + if isCustomVNET { + if a.OrchestratorProfile.OrchestratorType == Swarm { + return errors.New("bring your own VNET is not supported with Swarm, please let us know if you want this feature") + } + subscription, resourcegroup, vnetname, _, e := GetVNETSubnetIDComponents(a.MasterProfile.VnetSubnetID) + if e != nil { + return e + } + + for _, agentPool := range a.AgentPoolProfiles { + agentSubID, agentRG, agentVNET, _, err := GetVNETSubnetIDComponents(agentPool.VnetSubnetID) + if err != nil { + return err + } + if agentSubID != subscription || + agentRG != resourcegroup || + agentVNET != vnetname { + return errors.New("Multipe VNETS specified. The master profile and each agent pool must reference the same VNET (but it is ok to reference different subnets on that VNET)") + } + } + + // validate that the first master IP address has been set + if e = validateName(a.MasterProfile.FirstConsecutiveStaticIP, "MasterProfile.FirstConsecutiveStaticIP (with VNET Subnet specification)"); e != nil { + return e + } + } + return nil +} + +// GetVNETSubnetIDComponents extract subscription, resourcegroup, vnetname, subnetname from the vnetSubnetID +func GetVNETSubnetIDComponents(vnetSubnetID string) (string, string, string, string, error) { + vnetSubnetIDRegex := `^\/subscriptions\/([^\/]*)\/resourceGroups\/([^\/]*)\/providers\/Microsoft.Network\/virtualNetworks\/([^\/]*)\/subnets\/([^\/]*)$` + re, err := regexp.Compile(vnetSubnetIDRegex) + if err != nil { + return "", "", "", "", err + } + submatches := re.FindStringSubmatch(vnetSubnetID) + if len(submatches) != 4 { + return "", "", "", "", err + } + return submatches[1], submatches[2], submatches[3], submatches[4], nil +} diff --git a/acstgen/clusterdefinitions/dcos1.7.3original.json b/acstgen/clusterdefinitions/dcos1.7.3original.json new file mode 100644 index 0000000000..03fac84bd8 --- /dev/null +++ b/acstgen/clusterdefinitions/dcos1.7.3original.json @@ -0,0 +1,38 @@ +{ + "orchestratorProfile": { + "orchestratorType": "DCOS173" + }, + "masterProfile": { + "count": 1, + "dnsPrefix": "mgmtanhowe0926i", + "vmSize": "Standard_D2_v2" + }, + "agentPoolProfiles": [ + { + "name": "agentpool1", + "count": 5, + "vmSize": "Standard_D2_v2" + }, + { + "name": "agentpool2", + "count": 3, + "vmSize": "Standard_D2_v2", + "dnsPrefix": "appanhowe0926i", + "ports": [ + 80, + 443, + 8080 + ] + } + ], + "linuxProfile": { + "adminUsername": "azureuser", + "ssh": { + "publicKeys": [ + { + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8fhkh3jpHUQsrUIezFB5k4Rq9giJM8G1Cr0u2IRMiqG++nat5hbOr3gODpTA0h11q9bzb6nJtK7NtDzIHx+w3YNIVpcTGLiUEsfUbY53IHg7Nl/p3/gkST3g0R6BSL7Hg45SfyvpH7kwY30MoVHG/6P3go4SKlYoHXlgaaNr3fMwUTIeE9ofvyS3fcr6xxlsoB6luKuEs50h0NGsE4QEnbfSY4Yd/C1ucc3mEw+QFXBIsENHfHfZYrLNHm2L8MXYVmAH8k//5sFs4Migln9GiUgEQUT6uOjowsZyXBbXwfT11og+syPkAq4eqjiC76r0w6faVihdBYVoc/UcyupgH azureuser@linuxvm" + } + ] + } + } +} \ No newline at end of file diff --git a/acstgen/clusterdefinitions/dcos1.8.4original-stateful.json b/acstgen/clusterdefinitions/dcos1.8.4original-stateful.json new file mode 100644 index 0000000000..6d8d303087 --- /dev/null +++ b/acstgen/clusterdefinitions/dcos1.8.4original-stateful.json @@ -0,0 +1,34 @@ +{ + "orchestratorProfile": { + "orchestratorType": "DCOS" + }, + "masterProfile": { + "count": 3, + "dnsPrefix": "mgmtanhowe0928d", + "vmSize": "Standard_D2_v2" + }, + "agentPoolProfiles": [ + { + "name": "agtnodisks1", + "count": 1, + "vmSize": "Standard_D2_v2", + "isStateful": true + }, + { + "name": "agtnodisks2", + "count": 1, + "vmSize": "Standard_D2_v2", + "isStateful": true + } + ], + "linuxProfile": { + "adminUsername": "azureuser", + "ssh": { + "publicKeys": [ + { + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8fhkh3jpHUQsrUIezFB5k4Rq9giJM8G1Cr0u2IRMiqG++nat5hbOr3gODpTA0h11q9bzb6nJtK7NtDzIHx+w3YNIVpcTGLiUEsfUbY53IHg7Nl/p3/gkST3g0R6BSL7Hg45SfyvpH7kwY30MoVHG/6P3go4SKlYoHXlgaaNr3fMwUTIeE9ofvyS3fcr6xxlsoB6luKuEs50h0NGsE4QEnbfSY4Yd/C1ucc3mEw+QFXBIsENHfHfZYrLNHm2L8MXYVmAH8k//5sFs4Migln9GiUgEQUT6uOjowsZyXBbXwfT11og+syPkAq4eqjiC76r0w6faVihdBYVoc/UcyupgH azureuser@linuxvm" + } + ] + } + } +} \ No newline at end of file diff --git a/acstgen/clusterdefinitions/dcos1.8.4original-statefule.json b/acstgen/clusterdefinitions/dcos1.8.4original-statefule.json new file mode 100644 index 0000000000..34b1daeac1 --- /dev/null +++ b/acstgen/clusterdefinitions/dcos1.8.4original-statefule.json @@ -0,0 +1,70 @@ +{ + "orchestratorProfile": { + "orchestratorType": "DCOS" + }, + "masterProfile": { + "count": 3, + "dnsPrefix": "mgmtanhowe0928g", + "vmSize": "Standard_D2_v2" + }, + "agentPoolProfiles": [ + { + "name": "agtnodisks1", + "count": 1, + "vmSize": "Standard_D2_v2", + "isStateful": true + }, + { + "name": "agent128", + "count": 3, + "vmSize": "Standard_D2_v2", + "isStateful": true, + "diskSizesGB": [10, 10, 10, 10] + }, + { + "name": "agent1public", + "count": 3, + "vmSize": "Standard_D2_v2", + "dnsPrefix": "appanhowe0928g", + "isStateful": true, + "diskSizesGB": [1], + "ports": [ + 80, + 443, + 8080 + ] + }, + { + "name": "agtnodisks2", + "count": 1, + "vmSize": "Standard_D2_v2", + "isStateful": true + }, + { + "name": "agtnodisks3", + "count": 1, + "vmSize": "Standard_D2_v2", + "isStateful": true + }, + { + "name": "astateless1", + "count": 2, + "vmSize": "Standard_D2_v2" + }, + { + "name": "astateless2", + "count": 2, + "vmSize": "Standard_D2_v2" + } + ], + "linuxProfile": { + "adminUsername": "azureuser", + "ssh": { + "publicKeys": [ + { + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8fhkh3jpHUQsrUIezFB5k4Rq9giJM8G1Cr0u2IRMiqG++nat5hbOr3gODpTA0h11q9bzb6nJtK7NtDzIHx+w3YNIVpcTGLiUEsfUbY53IHg7Nl/p3/gkST3g0R6BSL7Hg45SfyvpH7kwY30MoVHG/6P3go4SKlYoHXlgaaNr3fMwUTIeE9ofvyS3fcr6xxlsoB6luKuEs50h0NGsE4QEnbfSY4Yd/C1ucc3mEw+QFXBIsENHfHfZYrLNHm2L8MXYVmAH8k//5sFs4Migln9GiUgEQUT6uOjowsZyXBbXwfT11og+syPkAq4eqjiC76r0w6faVihdBYVoc/UcyupgH azureuser@linuxvm" + } + ] + } + } +} \ No newline at end of file diff --git a/acstgen/clusterdefinitions/dcos1.8.4original.json b/acstgen/clusterdefinitions/dcos1.8.4original.json new file mode 100644 index 0000000000..307cdb6480 --- /dev/null +++ b/acstgen/clusterdefinitions/dcos1.8.4original.json @@ -0,0 +1,38 @@ +{ + "orchestratorProfile": { + "orchestratorType": "DCOS" + }, + "masterProfile": { + "count": 3, + "dnsPrefix": "mgmtanhowe0927i", + "vmSize": "Standard_D2_v2" + }, + "agentPoolProfiles": [ + { + "name": "agentprivate", + "count": 3, + "vmSize": "Standard_D2_v2" + }, + { + "name": "agentpublic", + "count": 3, + "vmSize": "Standard_D2_v2", + "dnsPrefix": "appanhowe0927i", + "ports": [ + 80, + 443, + 8080 + ] + } + ], + "linuxProfile": { + "adminUsername": "azureuser", + "ssh": { + "publicKeys": [ + { + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8fhkh3jpHUQsrUIezFB5k4Rq9giJM8G1Cr0u2IRMiqG++nat5hbOr3gODpTA0h11q9bzb6nJtK7NtDzIHx+w3YNIVpcTGLiUEsfUbY53IHg7Nl/p3/gkST3g0R6BSL7Hg45SfyvpH7kwY30MoVHG/6P3go4SKlYoHXlgaaNr3fMwUTIeE9ofvyS3fcr6xxlsoB6luKuEs50h0NGsE4QEnbfSY4Yd/C1ucc3mEw+QFXBIsENHfHfZYrLNHm2L8MXYVmAH8k//5sFs4Migln9GiUgEQUT6uOjowsZyXBbXwfT11og+syPkAq4eqjiC76r0w6faVihdBYVoc/UcyupgH azureuser@linuxvm" + } + ] + } + } +} \ No newline at end of file diff --git a/acstgen/clusterdefinitions/dcosnoiporiginal.json b/acstgen/clusterdefinitions/dcosnoiporiginal.json new file mode 100644 index 0000000000..f10d2e1f35 --- /dev/null +++ b/acstgen/clusterdefinitions/dcosnoiporiginal.json @@ -0,0 +1,38 @@ +{ + "orchestratorProfile": { + "orchestratorType": "DCOS" + }, + "masterProfile": { + "count": 5, + "dnsPrefix": "mgmtanhowe0926i", + "vmSize": "Standard_D2_v2" + }, + "agentPoolProfiles": [ + { + "name": "agentprivate", + "count": 3, + "vmSize": "Standard_D2_v2" + }, + { + "name": "agentpublic", + "count": 3, + "vmSize": "Standard_D2_v2", + "dnsPrefix": "appanhowe0926i", + "ports": [ + 80, + 443, + 8080 + ] + } + ], + "linuxProfile": { + "adminUsername": "azureuser", + "ssh": { + "publicKeys": [ + { + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8fhkh3jpHUQsrUIezFB5k4Rq9giJM8G1Cr0u2IRMiqG++nat5hbOr3gODpTA0h11q9bzb6nJtK7NtDzIHx+w3YNIVpcTGLiUEsfUbY53IHg7Nl/p3/gkST3g0R6BSL7Hg45SfyvpH7kwY30MoVHG/6P3go4SKlYoHXlgaaNr3fMwUTIeE9ofvyS3fcr6xxlsoB6luKuEs50h0NGsE4QEnbfSY4Yd/C1ucc3mEw+QFXBIsENHfHfZYrLNHm2L8MXYVmAH8k//5sFs4Migln9GiUgEQUT6uOjowsZyXBbXwfT11og+syPkAq4eqjiC76r0w6faVihdBYVoc/UcyupgH azureuser@linuxvm" + } + ] + } + } +} \ No newline at end of file diff --git a/acstgen/clusterdefinitions/swarm.json b/acstgen/clusterdefinitions/swarm.json new file mode 100644 index 0000000000..dc5925b29e --- /dev/null +++ b/acstgen/clusterdefinitions/swarm.json @@ -0,0 +1,38 @@ +{ + "orchestratorProfile": { + "orchestratorType": "Swarm" + }, + "masterProfile": { + "count": 1, + "dnsPrefix": "mgmtanhowe0927c", + "vmSize": "Standard_D2_v2" + }, + "agentPoolProfiles": [ + { + "name": "agentpool1", + "count": 3, + "vmSize": "Standard_D2_v2", + "dnsPrefix": "appanhowe0927c", + "ports": [ + 80, + 443, + 8080 + ] + }, + { + "name": "agentpool2", + "count": 3, + "vmSize": "Standard_D2_v2" + } + ], + "linuxProfile": { + "adminUsername": "azureuser", + "ssh": { + "publicKeys": [ + { + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8fhkh3jpHUQsrUIezFB5k4Rq9giJM8G1Cr0u2IRMiqG++nat5hbOr3gODpTA0h11q9bzb6nJtK7NtDzIHx+w3YNIVpcTGLiUEsfUbY53IHg7Nl/p3/gkST3g0R6BSL7Hg45SfyvpH7kwY30MoVHG/6P3go4SKlYoHXlgaaNr3fMwUTIeE9ofvyS3fcr6xxlsoB6luKuEs50h0NGsE4QEnbfSY4Yd/C1ucc3mEw+QFXBIsENHfHfZYrLNHm2L8MXYVmAH8k//5sFs4Migln9GiUgEQUT6uOjowsZyXBbXwfT11og+syPkAq4eqjiC76r0w6faVihdBYVoc/UcyupgH azureuser@linuxvm" + } + ] + } + } +} \ No newline at end of file diff --git a/acstgen/clusterdefinitions/vnet/azuredeploy.json b/acstgen/clusterdefinitions/vnet/azuredeploy.json new file mode 100644 index 0000000000..ce845ba2f8 --- /dev/null +++ b/acstgen/clusterdefinitions/vnet/azuredeploy.json @@ -0,0 +1,36 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { }, + "variables": { }, + "resources": [ + { + "apiVersion": "2016-03-30", + "location": "[resourceGroup().location]", + "name": "ExampleCustomVNET", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "10.100.0.0/24", + "10.200.0.0/24" + ] + }, + "subnets": [ + { + "name": "ExampleMasterSubnet", + "properties": { + "addressPrefix": "10.100.0.0/24" + } + }, + { + "name": "ExampleAgentSubnet", + "properties": { + "addressPrefix": "10.200.0.0/24" + } + } + ] + }, + "type": "Microsoft.Network/virtualNetworks" + } + ] +} diff --git a/acstgen/clusterdefinitions/vnet/dcosvnetoriginal.json b/acstgen/clusterdefinitions/vnet/dcosvnetoriginal.json new file mode 100644 index 0000000000..74f9f2c516 --- /dev/null +++ b/acstgen/clusterdefinitions/vnet/dcosvnetoriginal.json @@ -0,0 +1,42 @@ +{ + "orchestratorProfile": { + "orchestratorType": "DCOS" + }, + "masterProfile": { + "count": 5, + "dnsPrefix": "mgmtanhowe0927h", + "vmSize": "Standard_D2_v2", + "vnetSubnetId": "/subscriptions/b52fce95-de5f-4b37-afca-db203a5d0b6a/resourceGroups/anhoweExampleRG/providers/Microsoft.Network/virtualNetworks/ExampleCustomVNET/subnets/ExampleMasterSubnet", + "firstConsecutiveStaticIP": "10.100.0.5" + }, + "agentPoolProfiles": [ + { + "name": "agentPrivate", + "count": 3, + "vmSize": "Standard_D2_v2", + "vnetSubnetId": "/subscriptions/b52fce95-de5f-4b37-afca-db203a5d0b6a/resourceGroups/anhoweExampleRG/providers/Microsoft.Network/virtualNetworks/ExampleCustomVNET/subnets/ExampleAgentSubnet" + }, + { + "name": "agentPublic", + "count": 3, + "vmSize": "Standard_D2_v2", + "dnsPrefix": "appanhowe0927h", + "vnetSubnetId": "/subscriptions/b52fce95-de5f-4b37-afca-db203a5d0b6a/resourceGroups/anhoweExampleRG/providers/Microsoft.Network/virtualNetworks/ExampleCustomVNET/subnets/ExampleAgentSubnet", + "ports": [ + 80, + 443, + 8080 + ] + } + ], + "linuxProfile": { + "adminUsername": "azureuser", + "ssh": { + "publicKeys": [ + { + "keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8fhkh3jpHUQsrUIezFB5k4Rq9giJM8G1Cr0u2IRMiqG++nat5hbOr3gODpTA0h11q9bzb6nJtK7NtDzIHx+w3YNIVpcTGLiUEsfUbY53IHg7Nl/p3/gkST3g0R6BSL7Hg45SfyvpH7kwY30MoVHG/6P3go4SKlYoHXlgaaNr3fMwUTIeE9ofvyS3fcr6xxlsoB6luKuEs50h0NGsE4QEnbfSY4Yd/C1ucc3mEw+QFXBIsENHfHfZYrLNHm2L8MXYVmAH8k//5sFs4Migln9GiUgEQUT6uOjowsZyXBbXwfT11og+syPkAq4eqjiC76r0w6faVihdBYVoc/UcyupgH azureuser@linuxvm" + } + ] + } + } +} \ No newline at end of file diff --git a/acstgen/clusterdefinitions/vnet/deploy.ps1 b/acstgen/clusterdefinitions/vnet/deploy.ps1 new file mode 100644 index 0000000000..dd4a5c7144 --- /dev/null +++ b/acstgen/clusterdefinitions/vnet/deploy.ps1 @@ -0,0 +1,9 @@ +$VerbosePreference="Continue" +$deployName="anhoweExampleRG" +$RGName=$deployName +$SubscriptionId="b52fce95-de5f-4b37-afca-db203a5d0b6a" +Set-AzureRmContext -SubscriptionId $SubscriptionId +$locName="West US" +$templateFile = "azuredeploy.json" +New-AzureRmResourceGroup -Name $RGName -Location $locName -Force +New-AzureRmResourceGroupDeployment -Name $deployName -ResourceGroupName $RGName -TemplateFile $templateFile diff --git a/acstgen/clustertemplate/doc.go b/acstgen/clustertemplate/doc.go new file mode 100644 index 0000000000..f3f355ca03 --- /dev/null +++ b/acstgen/clustertemplate/doc.go @@ -0,0 +1,2 @@ +// Package clustertemplate stores an experimental api model for acs +package clustertemplate // import "./clustertemplate" diff --git a/acstgen/clustertemplate/template.go b/acstgen/clustertemplate/template.go new file mode 100644 index 0000000000..5dd83c5d16 --- /dev/null +++ b/acstgen/clustertemplate/template.go @@ -0,0 +1,392 @@ +package clustertemplate + +import ( + "bytes" + "errors" + "fmt" + "hash/fnv" + "io/ioutil" + "math/rand" + "os" + "path" + "text/template" + + "./../api/vlabs" +) + +const ( + agentOutputs = "agentoutputs.t" + agentParams = "agentparams.t" + dcosAgentResources = "dcosagentresources.t" + dcosAgentResourcesDisks = "dcosagentresourcesdisks.t" + dcosAgentVars = "dcosagentvars.t" + dcosBaseFile = "dcosbase.t" + dcosCustomData173 = "dcoscustomdata173.t" + dcosCustomData184 = "dcoscustomdata184.t" + dcosMasterResources = "dcosmasterresources.t" + dcosMasterVars = "dcosmastervars.t" + masterOutputs = "masteroutputs.t" + masterParams = "masterparams.t" + swarmBaseFile = "swarmbase.t" + swarmAgentCustomData = "swarmagentcustomdata.t" + swarmAgentResources = "swarmagentresources.t" + swarmAgentVars = "swarmagentvars.t" + swarmMasterCustomData = "swarmmastercustomdata.t" + swarmMasterResources = "swarmmasterresources.t" + swarmMasterVars = "swarmmastervars.t" +) + +var dcosTemplateFiles = []string{agentOutputs, agentParams, dcosAgentResources, dcosAgentResourcesDisks, dcosAgentVars, dcosBaseFile, dcosCustomData173, dcosCustomData184, dcosMasterResources, dcosMasterVars, masterOutputs, masterParams} +var swarmTemplateFiles = []string{agentOutputs, agentParams, masterParams, swarmBaseFile, swarmAgentCustomData, swarmAgentResources, swarmAgentVars, swarmBaseFile, masterOutputs, swarmMasterCustomData, swarmMasterResources, swarmMasterVars} + +// VerifyFiles verifies that the required template files exist +func VerifyFiles(partsDirectory string) error { + for _, file := range append(dcosTemplateFiles, swarmTemplateFiles...) { + templateFile := path.Join(partsDirectory, file) + if _, err := os.Stat(templateFile); os.IsNotExist(err) { + return fmt.Errorf("template file %s does not exist, did you specify the correct template directory?", templateFile) + } + } + return nil +} + +// GenerateTemplate generates the template from the API Model +func GenerateTemplate(acsCluster *vlabs.AcsCluster, partsDirectory string) (string, error) { + var err error + var templ *template.Template + + templateMap := template.FuncMap{ + "IsDCOS173": func() bool { + return acsCluster.OrchestratorProfile.OrchestratorType == vlabs.DCOS173 + }, + "IsDCOS184": func() bool { + return acsCluster.OrchestratorProfile.OrchestratorType == vlabs.DCOS184 || + acsCluster.OrchestratorProfile.OrchestratorType == vlabs.DCOS + }, + "IsPublic": func(ports []int) bool { + return len(ports) > 0 + }, + "GetVNETSubnetDependencies": func() string { + return getVNETSubnetDependencies(acsCluster) + }, + "GetLBRules": func(name string, ports []int) string { + return getLBRules(name, ports) + }, + "GetProbes": func(ports []int) string { + return getProbes(ports) + }, + "GetSecurityRules": func(ports []int) string { + return getSecurityRules(ports) + }, + "GetMasterRolesFileContents": func() string { + return getMasterRolesFileContents() + }, + "GetAgentRolesFileContents": func(ports []int) string { + return getAgentRolesFileContents(ports) + }, + "GetDCOSCustomDataPublicIPStr": func() string { + return getDCOSCustomDataPublicIPStr(acsCluster.OrchestratorProfile.OrchestratorType, acsCluster.MasterProfile.Count) + }, + "GetDCOSGUID": func() string { + return getPackageGUID(acsCluster.OrchestratorProfile.OrchestratorType, acsCluster.MasterProfile.Count) + }, + "GetLinuxProfileFirstSSHPublicKey": func() string { + return acsCluster.LinuxProfile.SSH.PublicKeys[0].KeyData + }, + "GetUniqueNameSuffix": func() string { + return generateUniqueNameSuffix(acsCluster) + }, + "GetVNETAddressPrefixes": func() string { + return getVNETAddressPrefixes(acsCluster) + }, + "GetVNETSubnets": func(addNSG bool) string { + return getVNETSubnets(acsCluster, addNSG) + }, + "GetDataDisks": func(profile *vlabs.AgentPoolProfile) string { + return getDataDisks(profile) + }, + // inspired by http://stackoverflow.com/questions/18276173/calling-a-template-with-several-pipeline-parameters/18276968#18276968 + "dict": func(values ...interface{}) (map[string]interface{}, error) { + if len(values)%2 != 0 { + return nil, errors.New("invalid dict call") + } + dict := make(map[string]interface{}, len(values)/2) + for i := 0; i < len(values); i += 2 { + key, ok := values[i].(string) + if !ok { + return nil, errors.New("dict keys must be strings") + } + dict[key] = values[i+1] + } + return dict, nil + }, + } + templ = template.New("acs template").Funcs(templateMap) + + var files []string + var baseFile string + if isDCOS(acsCluster) { + files = dcosTemplateFiles + baseFile = dcosBaseFile + } else if isSwarm(acsCluster) { + files = swarmTemplateFiles + baseFile = swarmBaseFile + } + + for _, file := range files { + templateFile := path.Join(partsDirectory, file) + bytes, e := ioutil.ReadFile(templateFile) + if e != nil { + return "", fmt.Errorf("Error reading file %s: %s", templateFile, e.Error()) + } + if _, err = templ.New(file).Parse(string(bytes)); err != nil { + return "", err + } + } + var b bytes.Buffer + if err = templ.ExecuteTemplate(&b, baseFile, acsCluster); err != nil { + return "", err + } + + return b.String(), nil +} + +func generateUniqueNameSuffix(acsCluster *vlabs.AcsCluster) string { + uniqueNameSuffixSize := 8 + // the name suffix uniquely identifies the cluster and is generated off a hash + // from the master dns name + h := fnv.New64a() + h.Write([]byte(acsCluster.MasterProfile.DNSPrefix)) + rand.Seed(int64(h.Sum64())) + return fmt.Sprintf("%08d", rand.Uint32())[:uniqueNameSuffixSize] +} + +func getPackageGUID(orchestratorType string, masterCount int) string { + if orchestratorType == vlabs.DCOS || orchestratorType == vlabs.DCOS184 { + switch masterCount { + case 1: + return "5ac6a7d060584c58c704e1f625627a591ecbde4e" + case 3: + return "42bd1d74e9a2b23836bd78919c716c20b98d5a0e" + case 5: + return "97947a91e2c024ed4f043bfcdad49da9418d3095" + } + } else if orchestratorType == vlabs.DCOS173 { + switch masterCount { + case 1: + return "6b604c1331c2b8b52bb23d1ea8a8d17e0f2b7428" + case 3: + return "6af5097e7956962a3d4318d28fbf280a47305485" + case 5: + return "376e07e0dbad2af3da2c03bc92bb07e84b3dafd5" + } + } + return "" +} + +func getDCOSCustomDataPublicIPStr(orchestratorType string, masterCount int) string { + if orchestratorType == vlabs.DCOS || + orchestratorType == vlabs.DCOS173 || + orchestratorType == vlabs.DCOS184 { + var buf bytes.Buffer + for i := 0; i < masterCount; i++ { + buf.WriteString(fmt.Sprintf("reference(variables('masterVMNic')[%d]).ipConfigurations[0].properties.privateIPAddress,", i)) + if i < (masterCount - 1) { + buf.WriteString(`'\\\", \\\"', `) + } + } + return buf.String() + } + return "" +} + +func getVNETAddressPrefixes(acsCluster *vlabs.AcsCluster) string { + var buf bytes.Buffer + buf.WriteString(`"[variables('masterSubnet')]"`) + for _, agentProfile := range acsCluster.AgentPoolProfiles { + buf.WriteString(fmt.Sprintf(",\n \"[variables('%sSubnet')]\"", agentProfile.Name)) + } + return buf.String() +} + +func getVNETSubnetDependencies(acsCluster *vlabs.AcsCluster) string { + agentString := ` "[concat('Microsoft.Network/networkSecurityGroups/', variables('%sNSGName'))]"` + var buf bytes.Buffer + for index, agentProfile := range acsCluster.AgentPoolProfiles { + if index > 0 { + buf.WriteString(",\n") + } + buf.WriteString(fmt.Sprintf(agentString, agentProfile.Name)) + } + return buf.String() +} + +func getVNETSubnets(acsCluster *vlabs.AcsCluster, addNSG bool) string { + masterString := `{ + "name": "[variables('masterSubnetName')]", + "properties": { + "addressPrefix": "[variables('masterSubnet')]" + } + }` + agentString := ` { + "name": "[variables('%sSubnetName')]", + "properties": { + "addressPrefix": "[variables('%sSubnet')]" + } + }` + agentStringNSG := ` { + "name": "[variables('%sSubnetName')]", + "properties": { + "addressPrefix": "[variables('%sSubnet')]", + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('%sNSGName'))]" + } + } + }` + var buf bytes.Buffer + buf.WriteString(masterString) + for _, agentProfile := range acsCluster.AgentPoolProfiles { + buf.WriteString(",\n") + if addNSG { + buf.WriteString(fmt.Sprintf(agentStringNSG, agentProfile.Name, agentProfile.Name, agentProfile.Name)) + } else { + buf.WriteString(fmt.Sprintf(agentString, agentProfile.Name, agentProfile.Name)) + } + + } + return buf.String() +} + +func getLBRule(name string, port int) string { + return fmt.Sprintf(` { + "name": "LBRule%d", + "properties": { + "backendAddressPool": { + "id": "[concat(variables('%sLbID'), '/backendAddressPools/', variables('%sLbBackendPoolName'))]" + }, + "backendPort": %d, + "enableFloatingIP": false, + "frontendIPConfiguration": { + "id": "[variables('%sLbIPConfigID')]" + }, + "frontendPort": %d, + "idleTimeoutInMinutes": 5, + "loadDistribution": "Default", + "probe": { + "id": "[concat(variables('%sLbID'),'/probes/tcp%dProbe')]" + }, + "protocol": "tcp" + } + }`, port, name, name, port, name, port, name, port) +} + +func getLBRules(name string, ports []int) string { + var buf bytes.Buffer + for index, port := range ports { + if index > 0 { + buf.WriteString(",\n") + } + buf.WriteString(getLBRule(name, port)) + } + return buf.String() +} + +func getProbe(port int) string { + return fmt.Sprintf(` { + "name": "tcp%dProbe", + "properties": { + "intervalInSeconds": "5", + "numberOfProbes": "2", + "port": %d, + "protocol": "tcp" + } + }`, port, port) +} + +func getProbes(ports []int) string { + var buf bytes.Buffer + for index, port := range ports { + if index > 0 { + buf.WriteString(",\n") + } + buf.WriteString(getProbe(port)) + } + return buf.String() +} + +func getSecurityRule(port int, portIndex int) string { + return fmt.Sprintf(` { + "name": "Allow_%d", + "properties": { + "access": "Allow", + "description": "Allow traffic from the Internet to port %d", + "destinationAddressPrefix": "*", + "destinationPortRange": "%d", + "direction": "Inbound", + "priority": %d, + "protocol": "*", + "sourceAddressPrefix": "Internet", + "sourcePortRange": "*" + } + }`, port, port, port, vlabs.BaseLBPriority+portIndex) +} + +func getDataDisks(a *vlabs.AgentPoolProfile) string { + if !a.HasDisks() { + return "" + } + var buf bytes.Buffer + buf.WriteString("\"dataDisks\": [\n") + dataDisks := ` { + "createOption": "Empty", + "diskSizeGB": "%d", + "lun": %d, + "name": "[concat(variables('%sVMNamePrefix'), copyIndex(),'-datadisk%d')]", + "vhd": { + "uri": "[concat('http://',variables('storageAccountPrefixes')[mod(add(add(div(copyIndex(),variables('maxVMsPerStorageAccount')),variables('%sStorageAccountOffset')),variables('dataStorageAccountPrefixSeed')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(add(div(copyIndex(),variables('maxVMsPerStorageAccount')),variables('%sStorageAccountOffset')),variables('dataStorageAccountPrefixSeed')),variables('storageAccountPrefixesCount'))],variables('%sDataAccountName'),'.blob.core.windows.net/vhds/',variables('%sVMNamePrefix'),copyIndex(), '--datadisk%d.vhd')]" + } + }` + for i, diskSize := range a.DiskSizesGB { + if i > 0 { + buf.WriteString(",\n") + } + buf.WriteString(fmt.Sprintf(dataDisks, diskSize, i, a.Name, i, a.Name, a.Name, a.Name, a.Name, i)) + } + buf.WriteString("\n ],") + return buf.String() +} + +func getSecurityRules(ports []int) string { + var buf bytes.Buffer + for index, port := range ports { + if index > 0 { + buf.WriteString(",\n") + } + buf.WriteString(getSecurityRule(port, index)) + } + return buf.String() +} + +func getMasterRolesFileContents() string { + return `{\"content\": \"\", \"path\": \"/etc/mesosphere/roles/master\"}, {\"content\": \"\", \"path\": \"/etc/mesosphere/roles/azure_master\"},` +} + +func getAgentRolesFileContents(ports []int) string { + if len(ports) > 0 { + // public agents + return `{\"content\": \"\", \"path\": \"/etc/mesosphere/roles/slave_public\"},` + } else { + // private agents + return `{\"content\": \"\", \"path\": \"/etc/mesosphere/roles/slave\"},` + } +} + +func isDCOS(acsCluster *vlabs.AcsCluster) bool { + return acsCluster.OrchestratorProfile.OrchestratorType == vlabs.DCOS184 || + acsCluster.OrchestratorProfile.OrchestratorType == vlabs.DCOS || + acsCluster.OrchestratorProfile.OrchestratorType == vlabs.DCOS173 +} + +func isSwarm(acsCluster *vlabs.AcsCluster) bool { + return acsCluster.OrchestratorProfile.OrchestratorType == vlabs.Swarm +} diff --git a/acstgen/parts/agentoutputs.t b/acstgen/parts/agentoutputs.t new file mode 100644 index 0000000000..e2610ca34a --- /dev/null +++ b/acstgen/parts/agentoutputs.t @@ -0,0 +1,6 @@ +{{if IsPublic .Ports}} + "{{.Name}}FQDN": { + "type": "string", + "value": "[reference(concat('Microsoft.Network/publicIPAddresses/', variables('{{.Name}}IPAddressName'))).dnsSettings.fqdn]" + }, +{{end}} \ No newline at end of file diff --git a/acstgen/parts/agentparams.t b/acstgen/parts/agentparams.t new file mode 100644 index 0000000000..3b057817d7 --- /dev/null +++ b/acstgen/parts/agentparams.t @@ -0,0 +1,191 @@ + "{{.Name}}Count": { + "allowedValues": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100 + ], + "defaultValue": {{.Count}}, + "metadata": { + "description": "The number of Mesos agents for the cluster. This value can be from 1 to 100" + }, + "type": "int" + }, + "{{.Name}}VMSize": { + "allowedValues": [ + "Standard_A0", + "Standard_A1", + "Standard_A2", + "Standard_A3", + "Standard_A4", + "Standard_A5", + "Standard_A6", + "Standard_A7", + "Standard_A8", + "Standard_A9", + "Standard_A10", + "Standard_A11", + "Standard_D1", + "Standard_D2", + "Standard_D3", + "Standard_D4", + "Standard_D11", + "Standard_D12", + "Standard_D13", + "Standard_D14", + "Standard_D1_v2", + "Standard_D2_v2", + "Standard_D3_v2", + "Standard_D4_v2", + "Standard_D5_v2", + "Standard_D11_v2", + "Standard_D12_v2", + "Standard_D13_v2", + "Standard_D14_v2", + "Standard_G1", + "Standard_G2", + "Standard_G3", + "Standard_G4", + "Standard_G5", + "Standard_DS1", + "Standard_DS2", + "Standard_DS3", + "Standard_DS4", + "Standard_DS11", + "Standard_DS12", + "Standard_DS13", + "Standard_DS14", + "Standard_GS1", + "Standard_GS2", + "Standard_GS3", + "Standard_GS4", + "Standard_GS5" + ], + "defaultValue": "{{.VMSize}}", + "metadata": { + "description": "The size of the Virtual Machine." + }, + "type": "string" + }, +{{if .IsCustomVNET}} + "{{.Name}}VnetSubnetID": { + "defaultValue": "{{.VnetSubnetID}}", + "metadata": { + "description": "Sets the vnet subnet of agent pool '{{.Name}}'." + }, + "type": "string" + } +{{else}} + "{{.Name}}Subnet": { + "defaultValue": "{{.GetSubnet}}", + "metadata": { + "description": "Sets the subnet of agent pool '{{.Name}}'." + }, + "type": "string" + } +{{end}} +{{if IsPublic .Ports}} + ,"{{.Name}}EndpointDNSNamePrefix": { + "defaultValue": "{{.DNSPrefix}}", + "metadata": { + "description": "Sets the Domain name label for the agent pool IP Address. The concatenation of the domain name label and the regional DNS zone make up the fully qualified domain name associated with the public IP address." + }, + "type": "string" + } +{{end}} \ No newline at end of file diff --git a/acstgen/parts/dcosagentresources.t b/acstgen/parts/dcosagentresources.t new file mode 100644 index 0000000000..4e83dc9cf3 --- /dev/null +++ b/acstgen/parts/dcosagentresources.t @@ -0,0 +1,175 @@ + { + "apiVersion": "[variables('apiVersionDefault')]", + "location": "[resourceGroup().location]", + "name": "[variables('{{.Name}}NSGName')]", + "properties": { + "securityRules": [ + {{GetSecurityRules .Ports}} + ] + }, + "type": "Microsoft.Network/networkSecurityGroups" + }, + { + "apiVersion": "[variables('apiVersionStorage')]", + "copy": { + "count": "[variables('{{.Name}}StorageAccountsCount')]", + "name": "loop" + }, + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]" + ], + "location": "[variables('storageLocation')]", + "name": "[concat(variables('storageAccountPrefixes')[mod(add(copyIndex(),variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(copyIndex(),variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName'))]", + "properties": { + "accountType": "[variables('vmSizesMap')[variables('{{.Name}}VMSize')].storageAccountType]" + }, + "type": "Microsoft.Storage/storageAccounts" + }, +{{if IsPublic .Ports}} + { + "apiVersion": "[variables('apiVersionDefault')]", + "location": "[resourceGroup().location]", + "name": "[variables('{{.Name}}IPAddressName')]", + "properties": { + "dnsSettings": { + "domainNameLabel": "[variables('{{.Name}}EndpointDNSNamePrefix')]" + }, + "publicIPAllocationMethod": "Dynamic" + }, + "type": "Microsoft.Network/publicIPAddresses" + }, + { + "apiVersion": "[variables('apiVersionDefault')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('{{.Name}}IPAddressName'))]" + ], + "location": "[resourceGroup().location]", + "name": "[variables('{{.Name}}LbName')]", + "properties": { + "backendAddressPools": [ + { + "name": "[variables('{{.Name}}LbBackendPoolName')]" + } + ], + "frontendIPConfigurations": [ + { + "name": "[variables('{{.Name}}LbIPConfigName')]", + "properties": { + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('{{.Name}}IPAddressName'))]" + } + } + } + ], + "inboundNatRules": [], + "loadBalancingRules": [ + {{(GetLBRules .Name .Ports)}} + ], + "probes": [ + {{(GetProbes .Ports)}} + ] + }, + "type": "Microsoft.Network/loadBalancers" + }, +{{end}} + { + "apiVersion": "[variables('apiVersionDefault')]", + "dependsOn": [ + "[concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(0,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(0,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName'))]", + "[concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(1,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(1,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName'))]", + "[concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(2,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(2,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName'))]", + "[concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(3,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(3,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName'))]", + "[concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(4,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(4,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName'))]" +{{if .IsCustomVNET}} + ,"[concat('Microsoft.Network/networkSecurityGroups/', variables('{{.Name}}NSGName'))]" +{{else}} + ,"[variables('vnetID')]" +{{end}} +{{if IsPublic .Ports}} + ,"[concat('Microsoft.Network/loadBalancers/', variables('{{.Name}}LbName'))]" +{{end}} + ], + "location": "[resourceGroup().location]", + "name": "[concat(variables('{{.Name}}VMNamePrefix'), '-vmss')]", + "properties": { + "upgradePolicy": { + "mode": "Manual" + }, + "virtualMachineProfile": { + "networkProfile": { + "networkInterfaceConfigurations": [ + { + "name": "nic", + "properties": { +{{if .IsCustomVNET}} + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups/', variables('{{.Name}}NSGName'))]" + }, +{{end}} + "ipConfigurations": [ + { + "name": "nicipconfig", + "properties": { +{{if IsPublic .Ports}} + "loadBalancerBackendAddressPools": [ + { + "id": "[concat('/subscriptions/', subscription().subscriptionId,'/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Network/loadBalancers/', variables('{{.Name}}LbName'), '/backendAddressPools/',variables('{{.Name}}LbBackendPoolName'))]" + } + ], +{{end}} + "subnet": { + "id": "[variables('{{.Name}}VnetSubnetID')]" + } + } + } + ], + "primary": "true" + } + } + ] + }, + "osProfile": { + "adminUsername": "[variables('adminUsername')]", + "computerNamePrefix": "[variables('{{.Name}}VMNamePrefix')]", + "customData": "[base64(concat({{if IsDCOS173}}{{template "dcoscustomdata173.t" dict "DCOSCustomDataPublicIPStr" GetDCOSCustomDataPublicIPStr "DCOSGUID" GetDCOSGUID "RolesString" (GetAgentRolesFileContents .Ports)}}{{else if IsDCOS184}}{{template "dcoscustomdata184.t" dict "DCOSCustomDataPublicIPStr" GetDCOSCustomDataPublicIPStr "DCOSGUID" GetDCOSGUID "RolesString" (GetAgentRolesFileContents .Ports)}}{{end}}))]", + "linuxConfiguration": { + "disablePasswordAuthentication": "true", + "ssh": { + "publicKeys": [ + { + "keyData": "[parameters('sshRSAPublicKey')]", + "path": "[variables('sshKeyPath')]" + } + ] + } + } + }, + "storageProfile": { + "imageReference": { + "offer": "[variables('osImageOffer')]", + "publisher": "[variables('osImagePublisher')]", + "sku": "[variables('osImageSKU')]", + "version": "[variables('osImageVersion')]" + }, + "osDisk": { + "caching": "ReadOnly", + "createOption": "FromImage", + "name": "vmssosdisk", + "vhdContainers": [ + "[concat(reference(concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(0,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(0,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName')),variables('apiVersionStorage')).primaryEndpoints.blob,'osdisk')]", + "[concat(reference(concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(1,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(1,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName')),variables('apiVersionStorage')).primaryEndpoints.blob,'osdisk')]", + "[concat(reference(concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(2,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(2,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName')),variables('apiVersionStorage')).primaryEndpoints.blob,'osdisk')]", + "[concat(reference(concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(3,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(3,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName')),variables('apiVersionStorage')).primaryEndpoints.blob,'osdisk')]", + "[concat(reference(concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(4,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(4,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName')),variables('apiVersionStorage')).primaryEndpoints.blob,'osdisk')]" + ] + } + } + } + }, + "sku": { + "capacity": "[variables('{{.Name}}Count')]", + "name": "[variables('{{.Name}}VMSize')]", + "tier": "Standard" + }, + "type": "Microsoft.Compute/virtualMachineScaleSets" + } diff --git a/acstgen/parts/dcosagentresourcesdisks.t b/acstgen/parts/dcosagentresourcesdisks.t new file mode 100644 index 0000000000..ab754da992 --- /dev/null +++ b/acstgen/parts/dcosagentresourcesdisks.t @@ -0,0 +1,210 @@ + { + "apiVersion": "[variables('apiVersionDefault')]", + "location": "[resourceGroup().location]", + "name": "[variables('{{.Name}}NSGName')]", + "properties": { + "securityRules": [ + {{GetSecurityRules .Ports}} + ] + }, + "type": "Microsoft.Network/networkSecurityGroups" + }, + { + "apiVersion": "[variables('apiVersionDefault')]", + "copy": { + "count": "[variables('{{.Name}}Count')]", + "name": "loop" + }, + "dependsOn": [ +{{if .IsCustomVNET}} + "[concat('Microsoft.Network/networkSecurityGroups/', variables('{{.Name}}NSGName'))]" +{{else}} + "[variables('vnetID')]" +{{end}} +{{if IsPublic .Ports}} + ,"[variables('{{.Name}}LbID')]" +{{end}} + ], + "location": "[resourceGroup().location]", + "name": "[concat(variables('{{.Name}}VMNamePrefix'), 'nic-', copyIndex())]", + "properties": { +{{if .IsCustomVNET}} + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups/', variables('{{.Name}}NSGName'))]" + }, +{{end}} + "ipConfigurations": [ + { + "name": "ipConfigNode", + "properties": { +{{if IsPublic .Ports}} + "loadBalancerBackendAddressPools": [ + { + "id": "[concat('/subscriptions/', subscription().subscriptionId,'/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Network/loadBalancers/', variables('{{.Name}}LbName'), '/backendAddressPools/',variables('{{.Name}}LbBackendPoolName'))]" + } + ], +{{end}} + "privateIPAllocationMethod": "Dynamic", + "subnet": { + "id": "[variables('{{.Name}}VnetSubnetID')]" + } + } + } + ] + }, + "type": "Microsoft.Network/networkInterfaces" + }, + { + "apiVersion": "[variables('apiVersionStorage')]", + "copy": { + "count": "[variables('{{.Name}}StorageAccountsCount')]", + "name": "loop" + }, + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]" + ], + "location": "[variables('storageLocation')]", + "name": "[concat(variables('storageAccountPrefixes')[mod(add(copyIndex(),variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(copyIndex(),variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName'))]", + "properties": { + "accountType": "[variables('vmSizesMap')[variables('{{.Name}}VMSize')].storageAccountType]" + }, + "type": "Microsoft.Storage/storageAccounts" + }, +{{if .HasDisks}} + { + "apiVersion": "[variables('apiVersionStorage')]", + "copy": { + "count": "[variables('{{.Name}}StorageAccountsCount')]", + "name": "datadiskLoop" + }, + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]" + ], + "location": "[variables('storageLocation')]", + "name": "[concat(variables('storageAccountPrefixes')[mod(add(copyIndex(variables('dataStorageAccountPrefixSeed')),variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(copyIndex(variables('dataStorageAccountPrefixSeed')),variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}DataAccountName'))]", + "properties": { + "accountType": "[variables('vmSizesMap')[variables('{{.Name}}VMSize')].storageAccountType]" + }, + "type": "Microsoft.Storage/storageAccounts" + }, +{{end}} + { + "apiVersion": "[variables('apiVersionDefault')]", + "location": "[resourceGroup().location]", + "name": "[variables('{{.Name}}AvailabilitySet')]", + "properties": {}, + "type": "Microsoft.Compute/availabilitySets" + }, +{{if IsPublic .Ports}} + { + "apiVersion": "[variables('apiVersionDefault')]", + "location": "[resourceGroup().location]", + "name": "[variables('{{.Name}}IPAddressName')]", + "properties": { + "dnsSettings": { + "domainNameLabel": "[variables('{{.Name}}EndpointDNSNamePrefix')]" + }, + "publicIPAllocationMethod": "Dynamic" + }, + "type": "Microsoft.Network/publicIPAddresses" + }, + { + "apiVersion": "[variables('apiVersionDefault')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('{{.Name}}IPAddressName'))]" + ], + "location": "[resourceGroup().location]", + "name": "[variables('{{.Name}}LbName')]", + "properties": { + "backendAddressPools": [ + { + "name": "[variables('{{.Name}}LbBackendPoolName')]" + } + ], + "frontendIPConfigurations": [ + { + "name": "[variables('{{.Name}}LbIPConfigName')]", + "properties": { + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('{{.Name}}IPAddressName'))]" + } + } + } + ], + "inboundNatRules": [], + "loadBalancingRules": [ + {{(GetLBRules .Name .Ports)}} + ], + "probes": [ + {{(GetProbes .Ports)}} + ] + }, + "type": "Microsoft.Network/loadBalancers" + }, +{{end}} + { + "apiVersion": "[variables('apiVersionDefault')]", + "copy": { + "count": "[variables('{{.Name}}Count')]", + "name": "vmLoopNode" + }, + "dependsOn": [ + "[concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(div(copyIndex(),variables('maxVMsPerStorageAccount')),variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(div(copyIndex(),variables('maxVMsPerStorageAccount')),variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName'))]", +{{if .HasDisks}} + "[concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(add(div(copyIndex(),variables('maxVMsPerStorageAccount')),variables('{{.Name}}StorageAccountOffset')),variables('dataStorageAccountPrefixSeed')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(add(div(copyIndex(),variables('maxVMsPerStorageAccount')),variables('{{.Name}}StorageAccountOffset')),variables('dataStorageAccountPrefixSeed')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}DataAccountName'))]", +{{end}} + "[concat('Microsoft.Network/networkInterfaces/', variables('{{.Name}}VMNamePrefix'), 'nic-', copyIndex())]", + "[concat('Microsoft.Compute/availabilitySets/', variables('{{.Name}}AvailabilitySet'))]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(variables('{{.Name}}VMNamePrefix'), copyIndex())]", + "properties": { + "availabilitySet": { + "id": "[resourceId('Microsoft.Compute/availabilitySets',variables('{{.Name}}AvailabilitySet'))]" + }, + "hardwareProfile": { + "vmSize": "[variables('{{.Name}}VMSize')]" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces',concat(variables('{{.Name}}VMNamePrefix'), 'nic-', copyIndex()))]" + } + ] + }, + "osProfile": { + "adminUsername": "[variables('adminUsername')]", + "computername": "[concat(variables('{{.Name}}VMNamePrefix'), copyIndex())]", + "customData": "[base64(concat({{if IsDCOS173}}{{template "dcoscustomdata173.t" dict "DCOSCustomDataPublicIPStr" GetDCOSCustomDataPublicIPStr "DCOSGUID" GetDCOSGUID "RolesString" (GetAgentRolesFileContents .Ports)}}{{else if IsDCOS184}}{{template "dcoscustomdata184.t" dict "DCOSCustomDataPublicIPStr" GetDCOSCustomDataPublicIPStr "DCOSGUID" GetDCOSGUID "RolesString" (GetAgentRolesFileContents .Ports)}}{{end}}))]", + "linuxConfiguration": { + "disablePasswordAuthentication": "true", + "ssh": { + "publicKeys": [ + { + "keyData": "[parameters('sshRSAPublicKey')]", + "path": "[variables('sshKeyPath')]" + } + ] + } + } + }, + "storageProfile": { + {{GetDataDisks .}} + "imageReference": { + "offer": "[variables('osImageOffer')]", + "publisher": "[variables('osImagePublisher')]", + "sku": "[variables('osImageSKU')]", + "version": "[variables('osImageVersion')]" + }, + "osDisk": { + "caching": "ReadOnly", + "createOption": "FromImage", + "name": "[concat(variables('{{.Name}}VMNamePrefix'), copyIndex(),'-osdisk')]", + "vhd": { + "uri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(div(copyIndex(),variables('maxVMsPerStorageAccount')),variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(div(copyIndex(),variables('maxVMsPerStorageAccount')),variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName')),variables('apiVersionStorage')).primaryEndpoints.blob,'osdisk/', variables('{{.Name}}VMNamePrefix'), copyIndex(), '-osdisk.vhd')]" + } + } + } + }, + "type": "Microsoft.Compute/virtualMachines" + } diff --git a/acstgen/parts/dcosagentvars.t b/acstgen/parts/dcosagentvars.t new file mode 100644 index 0000000000..43ad051c45 --- /dev/null +++ b/acstgen/parts/dcosagentvars.t @@ -0,0 +1,28 @@ + "{{.Name}}StorageAccountOffset": "[mul(variables('maxStorageAccountsPerAgent'),variables('{{.Name}}Index'))]", + "{{.Name}}Count": "[parameters('{{.Name}}Count')]", +{{if .IsStateful}} + "{{.Name}}AvailabilitySet": "[concat('{{.Name}}-availabilitySet-', variables('nameSuffix'))]", + "{{.Name}}StorageAccountsCount": "[add(div(variables('{{.Name}}Count'), variables('maxVMsPerStorageAccount')), mod(add(mod(variables('{{.Name}}Count'), variables('maxVMsPerStorageAccount')),2), add(mod(variables('{{.Name}}Count'), variables('maxVMsPerStorageAccount')),1)))]", +{{else}} + "{{.Name}}StorageAccountsCount": "[variables('maxStorageAccountsPerAgent')]", +{{end}} + "{{.Name}}NSGID": "[resourceId('Microsoft.Network/networkSecurityGroups',variables('{{.Name}}NSGName'))]", + "{{.Name}}NSGName": "[concat(variables('orchestratorName'), '-{{.Name}}-nsg-', variables('nameSuffix'))]", + "{{.Name}}VMNamePrefix": "[concat(variables('orchestratorName'), '-{{.Name}}-', variables('nameSuffix'))]", + "{{.Name}}VMSize": "[parameters('{{.Name}}VMSize')]", +{{if .IsCustomVNET}} + "{{.Name}}VnetSubnetID": "[parameters('{{.Name}}VnetSubnetID')]", +{{else}} + "{{.Name}}Subnet": "[parameters('{{.Name}}Subnet')]", + "{{.Name}}SubnetName": "[concat(variables('orchestratorName'), '-{{.Name}}Subnet')]", + "{{.Name}}VnetSubnetID": "[concat(variables('vnetID'),'/subnets/',variables('{{.Name}}SubnetName'))]", +{{end}} +{{if IsPublic .Ports}} + "{{.Name}}EndpointDNSNamePrefix": "[tolower(parameters('{{.Name}}EndpointDNSNamePrefix'))]", + "{{.Name}}IPAddressName": "[concat(variables('orchestratorName'), '-agent-ip-', variables('{{.Name}}EndpointDNSNamePrefix'), '-', variables('nameSuffix'))]", + "{{.Name}}LbBackendPoolName": "[concat(variables('orchestratorName'), '-{{.Name}}-', variables('nameSuffix'))]", + "{{.Name}}LbID": "[resourceId('Microsoft.Network/loadBalancers',variables('{{.Name}}LbName'))]", + "{{.Name}}LbIPConfigID": "[concat(variables('{{.Name}}LbID'),'/frontendIPConfigurations/', variables('{{.Name}}LbIPConfigName'))]", + "{{.Name}}LbIPConfigName": "[concat(variables('orchestratorName'), '-{{.Name}}-', variables('nameSuffix'))]", + "{{.Name}}LbName": "[concat(variables('orchestratorName'), '-{{.Name}}-', variables('nameSuffix'))]", +{{end}} \ No newline at end of file diff --git a/acstgen/parts/dcosbase.t b/acstgen/parts/dcosbase.t new file mode 100644 index 0000000000..f1c6b400bb --- /dev/null +++ b/acstgen/parts/dcosbase.t @@ -0,0 +1,34 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + {{range .AgentPoolProfiles}}{{template "agentparams.t" .}},{{end}} + {{template "masterparams.t" .}} + }, + "variables": { + {{range $index, $agent := .AgentPoolProfiles}} + {{template "dcosagentvars.t" .}} + {{if .IsStateful}} + "{{.Name}}DataAccountName": "[concat(variables('storageAccountBaseName'), 'data{{$index}}')]", + {{end}} + "{{.Name}}Index": {{$index}}, + "{{.Name}}AccountName": "[concat(variables('storageAccountBaseName'), 'agnt{{$index}}')]", + {{end}} + + {{template "dcosmastervars.t" .}} + }, + "resources": [ + {{range .AgentPoolProfiles}} + {{if .IsStateful}} + {{template "dcosagentresourcesdisks.t" .}}, + {{else}} + {{template "dcosagentresources.t" .}}, + {{end}} + {{end}} + {{template "dcosmasterresources.t" .}} + ], + "outputs": { + {{range .AgentPoolProfiles}}{{template "agentoutputs.t" .}} + {{end}}{{template "masteroutputs.t" .}} + } +} diff --git a/acstgen/parts/dcoscustomdata173.t b/acstgen/parts/dcoscustomdata173.t new file mode 100644 index 0000000000..ee812b556f --- /dev/null +++ b/acstgen/parts/dcoscustomdata173.t @@ -0,0 +1 @@ +'#cloud-config\n\n', '{\"bootcmd\": [\"bash -c \\\"if [ ! -f /var/lib/sdb-gpt ];then echo DCOS-5890;parted -s /dev/sdb mklabel gpt;touch /var/lib/sdb-gpt;fi\\\"\"], \"disk_setup\": {\"ephemeral0\": {\"layout\": [50, 50], \"overwrite\": true, \"table_type\": \"gpt\"}}, \"fs_setup\": [{\"device\": \"ephemeral0.1\", \"filesystem\": \"ext4\", \"overwrite\": true}, {\"device\": \"ephemeral0.2\", \"filesystem\": \"ext4\", \"overwrite\": true}], \"mounts\": [[\"ephemeral0.1\", \"/var/lib/mesos\"], [\"ephemeral0.2\", \"/var/lib/docker\"]], \"runcmd\": [[\"ln\", \"-s\", \"/bin/rm\", \"/usr/bin/rm\"], [\"ln\", \"-s\", \"/bin/mkdir\", \"/usr/bin/mkdir\"], [\"ln\", \"-s\", \"/bin/tar\", \"/usr/bin/tar\"], [\"ln\", \"-s\", \"/bin/ln\", \"/usr/bin/ln\"], [\"ln\", \"-s\", \"/bin/cp\", \"/usr/bin/cp\"], [\"ln\", \"-s\", \"/bin/systemctl\", \"/usr/bin/systemctl\"], [\"ln\", \"-s\", \"/bin/mount\", \"/usr/bin/mount\"], [\"ln\", \"-s\", \"/bin/bash\", \"/usr/bin/bash\"], [\"systemctl\", \"stop\", \"resolvconf.service\"], [\"systemctl\", \"disable\", \"resolvconf.service\"], [\"systemctl\", \"stop\", \"lxc-net.service\"], [\"systemctl\", \"disable\", \"lxc-net.service\"], [\"systemctl\", \"mask\", \"lxc-net.service\"], \"curl -fLsSv --retry 20 -Y 100000 -y 60 -o /tmp/1.deb https://az837203.vo.msecnd.net/dcos-deps/libipset3_6.29-1_amd64.deb\", \"curl -fLsSv --retry 20 -Y 100000 -y 60 -o /tmp/2.deb https://az837203.vo.msecnd.net/dcos-deps/ipset_6.29-1_amd64.deb\", \"curl -fLsSv --retry 20 -Y 100000 -y 60 -o /tmp/3.deb https://az837203.vo.msecnd.net/dcos-deps/unzip_6.0-20ubuntu1_amd64.deb\", \"curl -fLsSv --retry 20 -Y 100000 -y 60 -o /tmp/4.deb https://az837203.vo.msecnd.net/dcos-deps/libltdl7_2.4.6-0.1_amd64.deb\", \"bash -c \\\"try=1;until dpkg -i /tmp/{1,2,3,4}.deb || ((try>9));do echo retry \\\\$((try++));sleep \\\\$((try*try));done\\\"\", [\"cp\", \"-p\", \"/etc/resolv.conf\", \"/tmp/resolv.conf\"], [\"rm\", \"-f\", \"/etc/resolv.conf\"], [\"cp\", \"-p\", \"/tmp/resolv.conf\", \"/etc/resolv.conf\"], [\"systemctl\", \"start\", \"dcos-docker-install.service\"], [\"systemctl\", \"start\", \"dcos-link-env.service\"], [\"systemctl\", \"start\", \"dcos-download.service\"], [\"systemctl\", \"start\", \"dcos-setup.service\"], [\"systemctl\", \"start\", \"dcos-config-writer.service\"]], \"write_files\": [{\"content\": \"https://az837203.vo.msecnd.net/dcos/testing\\n\", \"owner\": \"root\", \"path\": \"/etc/mesosphere/setup-flags/repository-url\", \"permissions\": \"0644\"}, {\"content\": \"BOOTSTRAP_ID=df308b6fc3bd91e1277baa5a3db928ae70964722\\n\", \"owner\": \"root\", \"path\": \"/etc/mesosphere/setup-flags/bootstrap-id\", \"permissions\": \"0644\"}, {\"content\": \"[\\\"dcos-config--setup_{{.DCOSGUID}}\\\", \\\"dcos-metadata--setup_{{.DCOSGUID}}\\\"]\\n\", \"owner\": \"root\", \"path\": \"/etc/mesosphere/setup-flags/cluster-packages.json\", \"permissions\": \"0644\"}, {\"content\": \"[Journal]\\nMaxLevelConsole=warning\\n\", \"owner\": \"root\", \"path\": \"/etc/systemd/journald.conf.d/dcos.conf\", \"permissions\": \"0644\"}, {\"content\": \"', variables('nameSuffix'),'\\n\", \"path\": \"/etc/mesosphere/cluster-id\", \"permissions\": \"0644\"}, {\"content\": \"\\nrexray:\\n loglevel: info\\n modules:\\n default-docker:\\n disabled: true\\n\", \"path\": \"/etc/rexray/config.yml\", \"permissions\": \"0644\"}, {\"content\": \"[Unit]\\nBefore=dcos.target\\n[Service]\\nType=oneshot\\nStandardOutput=journal+console\\nStandardError=journal+console\\nExecStartPre=/usr/bin/mkdir -p /etc/profile.d\\nExecStart=/usr/bin/ln -sf /opt/mesosphere/environment.export /etc/profile.d/dcos.sh\\n\", \"path\": \"/etc/systemd/system/dcos-link-env.service\", \"permissions\": \"0644\"}, {\"content\": \"[Unit]\\nAfter=network-online.target\\nWants=network-online.target\\nConditionPathExists=!/opt/mesosphere/\\n[Service]\\nEnvironmentFile=/etc/mesosphere/setup-flags/bootstrap-id\\nType=oneshot\\nStandardOutput=journal+console\\nStandardError=journal+console\\nExecStartPre=/usr/bin/curl -fLsSv --retry 20 -Y 100000 -y 60 -o /tmp/bootstrap.tar.xz https://az837203.vo.msecnd.net/dcos/testing/bootstrap/${BOOTSTRAP_ID}.bootstrap.tar.xz\\nExecStartPre=/usr/bin/mkdir -p /opt/mesosphere\\nExecStart=/usr/bin/tar -axf /tmp/bootstrap.tar.xz -C /opt/mesosphere\\nExecStartPost=-/usr/bin/rm -f /tmp/bootstrap.tar.xz\\n\", \"path\": \"/etc/systemd/system/dcos-download.service\", \"permissions\": \"0644\"}, {\"content\": \"[Unit]\\nRequires=dcos-download.service\\nAfter=dcos-download.service\\n[Service]\\nType=oneshot\\nStandardOutput=journal+console\\nStandardError=journal+console\\nEnvironmentFile=/opt/mesosphere/environment\\nExecStart=/opt/mesosphere/bin/pkgpanda setup --no-block-systemd\\n[Install]\\nWantedBy=multi-user.target\\n\", \"path\": \"/etc/systemd/system/dcos-setup.service\", \"permissions\": \"0644\"}, {\"content\": \"[Unit]\\nAfter=network-online.target\\nWants=network-online.target\\n[Service]\\nType=oneshot\\nEnvironment=DEBIAN_FRONTEND=noninteractive\\nStandardOutput=journal+console\\nStandardError=journal+console\\nExecStartPre=/usr/bin/curl -fLsSv --retry 20 -Y 100000 -y 60 -o /tmp/d.deb https://az837203.vo.msecnd.net/dcos-deps/docker-engine_1.11.2-0~xenial_amd64.deb\\nExecStart=/usr/bin/bash -c \\\"try=1;until dpkg -D3 -i /tmp/d.deb || ((try>9));do echo retry $((try++));sleep $((try*try));done;systemctl --now start docker;systemctl restart docker.socket\\\"\\n\", \"path\": \"/etc/systemd/system/dcos-docker-install.service\", \"permissions\": \"0644\"}, {\"content\": \"[Service]\\nRestart=always\\nStartLimitInterval=0\\nRestartSec=15\\nExecStart=\\nExecStart=/usr/bin/docker daemon -H fd:// --storage-driver=overlay\\n\", \"path\": \"/etc/systemd/system/docker.service.d/execstart.conf\", \"permissions\": \"0644\"}, {\"content\": \"[Unit]\\nPartOf=docker.service\\n[Socket]\\nListenStream=/var/run/docker.sock\\nSocketMode=0660\\nSocketUser=root\\nSocketGroup=docker\\nListenStream=2375\\nBindIPv6Only=both\\n[Install]\\nWantedBy=sockets.target\\n\", \"path\": \"/etc/systemd/system/docker.socket\", \"permissions\": \"0644\"}, {\"content\": \"[Unit]\\nRequires=dcos-setup.service\\nAfter=dcos-setup.service\\n[Service]\\nType=oneshot\\nEnvironmentFile=/etc/environment\\nEnvironmentFile=/opt/mesosphere/environment\\nExecStart=/usr/bin/bash -c \\\"echo -e \\\\\\\"127.0.0.1 localhost\\\\n$(detect_ip) $(hostname)\\\\\\\" > /etc/hosts\\\"\\n\", \"path\": \"/etc/systemd/system/dcos-config-writer.service\", \"permissions\": \"0644\"}, {\"content\": \"MESOS_CLUSTER=', variables('masterPublicIPAddressName'),'\\n\", \"path\": \"/etc/mesosphere/setup-packages/dcos-provider-azure--setup/etc/mesos-master-provider\"}, {\"content\": \"\\nADMINROUTER_ACTIVATE_AUTH_MODULE=', variables('oauthEnabled'),'\\n\", \"path\": \"/etc/mesosphere/setup-packages/dcos-provider-azure--setup/etc/adminrouter.env\"}, {\"content\": \"[\\\"', {{.DCOSCustomDataPublicIPStr}}'\\\"]\\n\", \"path\": \"/etc/mesosphere/setup-packages/dcos-provider-azure--setup/etc/master_list\"}, {\"content\": \"EXHIBITOR_BACKEND=AZURE\\nAZURE_CONTAINER=dcos-exhibitor\\nAZURE_PREFIX=', variables('masterPublicIPAddressName'),'\\n\", \"path\": \"/etc/mesosphere/setup-packages/dcos-provider-azure--setup/etc/exhibitor\"}, {\"content\": \"com.netflix.exhibitor.azure.account-name=', variables('masterStorageAccountName'),'\\ncom.netflix.exhibitor.azure.account-key=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('masterStorageAccountName')), '2015-06-15').key1,'\\n\", \"path\": \"/etc/mesosphere/setup-packages/dcos-provider-azure--setup/etc/exhibitor.properties\"}, {\"content\": \"{\\\"uiConfiguration\\\":{\\\"plugins\\\":{\\\"banner\\\":{\\\"enabled\\\":false,\\\"backgroundColor\\\":\\\"#1E232F\\\",\\\"foregroundColor\\\":\\\"#FFFFFF\\\",\\\"headerTitle\\\":null,\\\"headerContent\\\":null,\\\"footerContent\\\":null,\\\"imagePath\\\":null,\\\"dismissible\\\":null},\\\"branding\\\":{\\\"enabled\\\":false},\\\"external-links\\\": {\\\"enabled\\\": false},\\n\\n\\\"authentication\\\":{\\\"enabled\\\":false},\\n\\n\\\"oauth\\\":{\\\"enabled\\\":', variables('oauthEnabled'),',\\\"authHost\\\":\\\"https://dcos.auth0.com\\\"},\\n\\n\\n\\\"networking\\\":{\\\"enabled\\\":false},\\\"organization\\\":{\\\"enabled\\\":false},\\\"tracking\\\":{\\\"enabled\\\":false}}}}\\n\", \"path\": \"/etc/mesosphere/setup-packages/dcos-provider-azure--setup/etc/ui-config.json\"}, {\"content\": \"{}\", \"path\": \"/etc/mesosphere/setup-packages/dcos-provider-azure--setup/pkginfo.json\"}, {{.RolesString}} {\"content\": \"\", \"path\": \"/etc/mesosphere/roles/azure\"}]}' \ No newline at end of file diff --git a/acstgen/parts/dcoscustomdata184.t b/acstgen/parts/dcoscustomdata184.t new file mode 100644 index 0000000000..899a91d835 --- /dev/null +++ b/acstgen/parts/dcoscustomdata184.t @@ -0,0 +1 @@ +'#cloud-config\n\n', '{\"bootcmd\": [\"bash -c \\\"if [ ! -f /var/lib/sdb-gpt ];then echo DCOS-5890;parted -s /dev/sdb mklabel gpt;touch /var/lib/sdb-gpt;fi\\\"\"], \"disk_setup\": {\"ephemeral0\": {\"layout\": [50, 50], \"overwrite\": true, \"table_type\": \"gpt\"}}, \"fs_setup\": [{\"device\": \"ephemeral0.1\", \"filesystem\": \"ext4\", \"overwrite\": true}, {\"device\": \"ephemeral0.2\", \"filesystem\": \"ext4\", \"overwrite\": true}], \"mounts\": [[\"ephemeral0.1\", \"/var/lib/mesos\"], [\"ephemeral0.2\", \"/var/lib/docker\"]], \"runcmd\": [[\"ln\", \"-s\", \"/bin/rm\", \"/usr/bin/rm\"], [\"ln\", \"-s\", \"/bin/mkdir\", \"/usr/bin/mkdir\"], [\"ln\", \"-s\", \"/bin/tar\", \"/usr/bin/tar\"], [\"ln\", \"-s\", \"/bin/ln\", \"/usr/bin/ln\"], [\"ln\", \"-s\", \"/bin/cp\", \"/usr/bin/cp\"], [\"ln\", \"-s\", \"/bin/systemctl\", \"/usr/bin/systemctl\"], [\"ln\", \"-s\", \"/bin/mount\", \"/usr/bin/mount\"], [\"ln\", \"-s\", \"/bin/bash\", \"/usr/bin/bash\"], [\"ln\", \"-s\", \"/usr/sbin/useradd\", \"/usr/bin/useradd\"], [\"systemctl\", \"disable\", \"--now\", \"resolvconf.service\"], [\"systemctl\", \"mask\", \"--now\", \"lxc-net.service\"], \"curl -fLsSv --retry 20 -Y 100000 -y 60 -o /tmp/1.deb https://az837203.vo.msecnd.net/dcos-deps/libipset3_6.29-1_amd64.deb\", \"curl -fLsSv --retry 20 -Y 100000 -y 60 -o /tmp/2.deb https://az837203.vo.msecnd.net/dcos-deps/ipset_6.29-1_amd64.deb\", \"curl -fLsSv --retry 20 -Y 100000 -y 60 -o /tmp/3.deb https://az837203.vo.msecnd.net/dcos-deps/unzip_6.0-20ubuntu1_amd64.deb\", \"curl -fLsSv --retry 20 -Y 100000 -y 60 -o /tmp/4.deb https://az837203.vo.msecnd.net/dcos-deps/libltdl7_2.4.6-0.1_amd64.deb\", \"bash -c \\\"try=1;until dpkg -i /tmp/{1,2,3,4}.deb || ((try>9));do echo retry \\\\$((try++));sleep \\\\$((try*try));done\\\"\", [\"cp\", \"-p\", \"/etc/resolv.conf\", \"/tmp/resolv.conf\"], [\"rm\", \"-f\", \"/etc/resolv.conf\"], [\"cp\", \"-p\", \"/tmp/resolv.conf\", \"/etc/resolv.conf\"], [\"systemctl\", \"start\", \"dcos-docker-install.service\"], [\"systemctl\", \"start\", \"dcos-config-writer.service\"], [\"systemctl\", \"restart\", \"systemd-journald.service\"], [\"systemctl\", \"restart\", \"docker.service\"], [\"systemctl\", \"start\", \"dcos-link-env.service\"], [\"systemctl\", \"enable\", \"dcos-setup.service\"], [\"systemctl\", \"--no-block\", \"start\", \"dcos-setup.service\"]], \"write_files\": [{\"content\": \"https://dcosio.azureedge.net/dcos/testing\\n\", \"owner\": \"root\", \"path\": \"/etc/mesosphere/setup-flags/repository-url\", \"permissions\": \"0644\"}, {\"content\": \"BOOTSTRAP_ID=5b4aa43610c57ee1d60b4aa0751a1fb75824c083\\n\", \"owner\": \"root\", \"path\": \"/etc/mesosphere/setup-flags/bootstrap-id\", \"permissions\": \"0644\"}, {\"content\": \"[\\\"dcos-config--setup_{{.DCOSGUID}}\\\", \\\"dcos-metadata--setup_{{.DCOSGUID}}\\\"]\\n\", \"owner\": \"root\", \"path\": \"/etc/mesosphere/setup-flags/cluster-packages.json\", \"permissions\": \"0644\"}, {\"content\": \"[Journal]\\nMaxLevelConsole=warning\\nRateLimitInterval=1s\\nRateLimitBurst=20000\\n\", \"owner\": \"root\", \"path\": \"/etc/systemd/journald.conf.d/dcos.conf\", \"permissions\": \"0644\"}, {\"content\": \"rexray:\\n loglevel: info\\n modules:\\n default-admin:\\n host: tcp://127.0.0.1:61003\\n default-docker:\\n disabled: true\\n\", \"path\": \"/etc/rexray/config.yml\", \"permissions\": \"0644\"}, {\"content\": \"[Unit]\\nAfter=network-online.target\\nWants=network-online.target\\n[Service]\\nType=oneshot\\nEnvironment=DEBIAN_FRONTEND=noninteractive\\nStandardOutput=journal+console\\nStandardError=journal+console\\nExecStartPre=/usr/bin/curl -fLsSv --retry 20 -Y 100000 -y 60 -o /tmp/d.deb https://az837203.vo.msecnd.net/dcos-deps/docker-engine_1.11.2-0~xenial_amd64.deb\\nExecStart=/usr/bin/bash -c \\\"try=1;until dpkg -D3 -i /tmp/d.deb || ((try>9));do echo retry $((try++));sleep $((try*try));done;systemctl --now start docker;systemctl restart docker.socket\\\"\\n\", \"path\": \"/etc/systemd/system/dcos-docker-install.service\", \"permissions\": \"0644\"}, {\"content\": \"[Service]\\nRestart=always\\nStartLimitInterval=0\\nRestartSec=15\\nExecStartPre=-/sbin/ip link del docker0\\nExecStart=\\nExecStart=/usr/bin/docker daemon -H fd:// --storage-driver=overlay\\n\", \"path\": \"/etc/systemd/system/docker.service.d/execstart.conf\", \"permissions\": \"0644\"}, {\"content\": \"[Unit]\\nPartOf=docker.service\\n[Socket]\\nListenStream=/var/run/docker.sock\\nSocketMode=0660\\nSocketUser=root\\nSocketGroup=docker\\nListenStream=2375\\nBindIPv6Only=both\\n[Install]\\nWantedBy=sockets.target\\n\", \"path\": \"/etc/systemd/system/docker.socket\", \"permissions\": \"0644\"}, {\"content\": \"[Unit]\\nRequires=dcos-setup.service\\nAfter=dcos-setup.service\\n[Service]\\nType=oneshot\\nEnvironmentFile=/etc/environment\\nEnvironmentFile=/opt/mesosphere/environment\\nExecStart=/usr/bin/bash -c \\\"echo $(detect_ip) $(hostname) > /etc/hosts\\\"\\n\", \"path\": \"/etc/systemd/system/dcos-config-writer.service\", \"permissions\": \"0644\"}, {\"content\": \"MESOS_CLUSTER=', variables('masterPublicIPAddressName'),'\\n\", \"path\": \"/etc/mesosphere/setup-packages/dcos-provider-azure--setup/etc/mesos-master-provider\"}, {\"content\": \"ADMINROUTER_ACTIVATE_AUTH_MODULE=', variables('oauthEnabled'),'\\n\", \"path\": \"/etc/mesosphere/setup-packages/dcos-provider-azure--setup/etc/adminrouter.env\"}, {\"content\": \"[\\\"', {{.DCOSCustomDataPublicIPStr}}'\\\"]\\n\", \"path\": \"/etc/mesosphere/setup-packages/dcos-provider-azure--setup/etc/master_list\"}, {\"content\": \"EXHIBITOR_BACKEND=AZURE\\nAZURE_CONTAINER=dcos-exhibitor\\nAZURE_PREFIX=', variables('masterPublicIPAddressName'),'\\n\", \"path\": \"/etc/mesosphere/setup-packages/dcos-provider-azure--setup/etc/exhibitor\"}, {\"content\": \"com.netflix.exhibitor.azure.account-name=', variables('masterStorageAccountExhibitorName'),'\\ncom.netflix.exhibitor.azure.account-key=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('masterStorageAccountExhibitorName')), '2015-06-15').key1,'\\n\", \"path\": \"/etc/mesosphere/setup-packages/dcos-provider-azure--setup/etc/exhibitor.properties\"}, {\"content\": \"{\\\"uiConfiguration\\\":{\\\"plugins\\\":{\\\"banner\\\":{\\\"enabled\\\":false,\\\"backgroundColor\\\":\\\"#1E232F\\\",\\\"foregroundColor\\\":\\\"#FFFFFF\\\",\\\"headerTitle\\\":null,\\\"headerContent\\\":null,\\\"footerContent\\\":null,\\\"imagePath\\\":null,\\\"dismissible\\\":null},\\\"branding\\\":{\\\"enabled\\\":false},\\\"external-links\\\": {\\\"enabled\\\": false},\\n\\n\\\"authentication\\\":{\\\"enabled\\\":false},\\n\\n\\\"oauth\\\":{\\\"enabled\\\":', variables('oauthEnabled'),',\\\"authHost\\\":\\\"https://dcos.auth0.com\\\"},\\n\\n\\n\\\"tracking\\\":{\\\"enabled\\\":false}}}}\\n\", \"path\": \"/etc/mesosphere/setup-packages/dcos-provider-azure--setup/etc/ui-config.json\"}, {\"content\": \"{}\", \"path\": \"/etc/mesosphere/setup-packages/dcos-provider-azure--setup/pkginfo.json\"}, {\"content\": \"[Unit]\\nBefore=dcos.target\\n[Service]\\nType=oneshot\\nStandardOutput=journal+console\\nStandardError=journal+console\\nExecStartPre=/usr/bin/mkdir -p /etc/profile.d\\nExecStart=/usr/bin/ln -sf /opt/mesosphere/environment.export /etc/profile.d/dcos.sh\\n\", \"path\": \"/etc/systemd/system/dcos-link-env.service\", \"permissions\": \"0644\"}, {\"content\": \"[Unit]\\nDescription=Pkgpanda: Download DC/OS to this host.\\nAfter=network-online.target\\nWants=network-online.target\\nConditionPathExists=!/opt/mesosphere/\\n[Service]\\nEnvironmentFile=/etc/mesosphere/setup-flags/bootstrap-id\\nType=oneshot\\nStandardOutput=journal+console\\nStandardError=journal+console\\nExecStartPre=/usr/bin/curl --keepalive-time 2 -fLsSv --retry 20 -Y 100000 -y 60 -o /tmp/bootstrap.tar.xz https://dcosio.azureedge.net/dcos/testing/bootstrap/${BOOTSTRAP_ID}.bootstrap.tar.xz\\nExecStartPre=/usr/bin/mkdir -p /opt/mesosphere\\nExecStart=/usr/bin/tar -axf /tmp/bootstrap.tar.xz -C /opt/mesosphere\\nExecStartPost=-/usr/bin/rm -f /tmp/bootstrap.tar.xz\\n\", \"path\": \"/etc/systemd/system/dcos-download.service\", \"permissions\": \"0644\"}, {\"content\": \"[Unit]\\nDescription=Pkgpanda: Specialize DC/OS for this host.\\nRequires=dcos-download.service\\nAfter=dcos-download.service\\n[Service]\\nType=oneshot\\nStandardOutput=journal+console\\nStandardError=journal+console\\nEnvironmentFile=/opt/mesosphere/environment\\nExecStart=/opt/mesosphere/bin/pkgpanda setup --no-block-systemd\\n[Install]\\nWantedBy=multi-user.target\\n\", \"path\": \"/etc/systemd/system/dcos-setup.service\", \"permissions\": \"0644\"}, {{.RolesString}} {\"content\": \"\", \"path\": \"/etc/mesosphere/roles/azure\"}]}' \ No newline at end of file diff --git a/acstgen/parts/dcosmasterresources.t b/acstgen/parts/dcosmasterresources.t new file mode 100644 index 0000000000..7777206b4d --- /dev/null +++ b/acstgen/parts/dcosmasterresources.t @@ -0,0 +1,268 @@ + { + "apiVersion": "[variables('apiVersionStorage')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]" + ], + "location": "[variables('storageLocation')]", + "name": "[variables('masterStorageAccountName')]", + "properties": { + "accountType": "[variables('vmSizesMap')[variables('masterVMSize')].storageAccountType]" + }, + "type": "Microsoft.Storage/storageAccounts" + }, + { + "apiVersion": "[variables('apiVersionStorage')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]" + ], + "location": "[variables('storageLocation')]", + "name": "[variables('masterStorageAccountExhibitorName')]", + "properties": { + "accountType": "Standard_LRS" + }, + "type": "Microsoft.Storage/storageAccounts" + }, +{{if not .MasterProfile.IsCustomVNET}} + { + "apiVersion": "[variables('apiVersionDefault')]", + "dependsOn": [ + {{GetVNETSubnetDependencies}} + ], + "location": "[resourceGroup().location]", + "name": "[variables('virtualNetworkName')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + {{GetVNETAddressPrefixes}} + ] + }, + "subnets": [ + {{GetVNETSubnets true}} + ] + }, + "type": "Microsoft.Network/virtualNetworks" + }, +{{end}} + { + "apiVersion": "[variables('apiVersionDefault')]", + "location": "[resourceGroup().location]", + "name": "[variables('masterAvailabilitySet')]", + "properties": {}, + "type": "Microsoft.Compute/availabilitySets" + }, + { + "apiVersion": "[variables('apiVersionDefault')]", + "location": "[resourceGroup().location]", + "name": "[variables('masterPublicIPAddressName')]", + "properties": { + "dnsSettings": { + "domainNameLabel": "[variables('masterEndpointDNSNamePrefix')]" + }, + "publicIPAllocationMethod": "Dynamic" + }, + "type": "Microsoft.Network/publicIPAddresses" + }, + { + "apiVersion": "[variables('apiVersionDefault')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]" + ], + "location": "[resourceGroup().location]", + "name": "[variables('masterLbName')]", + "properties": { + "backendAddressPools": [ + { + "name": "[variables('masterLbBackendPoolName')]" + } + ], + "frontendIPConfigurations": [ + { + "name": "[variables('masterLbIPConfigName')]", + "properties": { + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('masterPublicIPAddressName'))]" + } + } + } + ] + }, + "type": "Microsoft.Network/loadBalancers" + }, + { + "apiVersion": "[variables('apiVersionDefault')]", + "copy": { + "count": "[variables('masterCount')]", + "name": "masterLbLoopNode" + }, + "dependsOn": [ + "[variables('masterLbID')]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(variables('masterLbName'), '/', 'SSH-', variables('masterVMNamePrefix'), copyIndex())]", + "properties": { + "backendPort": 22, + "enableFloatingIP": false, + "frontendIPConfiguration": { + "id": "[variables('masterLbIPConfigID')]" + }, + "frontendPort": "[copyIndex(2200)]", + "protocol": "tcp" + }, + "type": "Microsoft.Network/loadBalancers/inboundNatRules" + }, + { + "apiVersion": "[variables('apiVersionDefault')]", + "location": "[resourceGroup().location]", + "name": "[variables('masterNSGName')]", + "properties": { + "securityRules": [ + { + "name": "ssh", + "properties": { + "access": "Allow", + "description": "Allow SSH", + "destinationAddressPrefix": "*", + "destinationPortRange": "22", + "direction": "Inbound", + "priority": 200, + "protocol": "Tcp", + "sourceAddressPrefix": "*", + "sourcePortRange": "*" + } + } + ] + }, + "type": "Microsoft.Network/networkSecurityGroups" + }, + { + "apiVersion": "[variables('apiVersionDefault')]", + "copy": { + "count": "[variables('masterCount')]", + "name": "nicLoopNode" + }, + "dependsOn": [ +{{if not .MasterProfile.IsCustomVNET}} + "[variables('vnetID')]", +{{end}} + "[variables('masterLbID')]", + "[concat(variables('masterLbID'),'/inboundNatRules/SSH-',variables('masterVMNamePrefix'),copyIndex())]", + "[variables('masterNSGID')]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(variables('masterVMNamePrefix'), 'nic-', copyIndex())]", + "properties": { + "ipConfigurations": [ + { + "name": "ipConfigNode", + "properties": { + "loadBalancerBackendAddressPools": [ + { + "id": "[concat(variables('masterLbID'), '/backendAddressPools/', variables('masterLbBackendPoolName'))]" + } + ], + "loadBalancerInboundNatRules": [ + { + "id": "[concat(variables('masterLbID'),'/inboundNatRules/SSH-',variables('masterVMNamePrefix'),copyIndex())]" + } + ], + "privateIPAddress": "[concat(variables('masterFirstAddrPrefix'), copyIndex(int(variables('masterFirstAddrOctet4'))))]", + "privateIPAllocationMethod": "Static", + "subnet": { + "id": "[variables('masterVnetSubnetID')]" + } + } + } + ], + "networkSecurityGroup": { + "id": "[variables('masterNSGID')]" + } + }, + "type": "Microsoft.Network/networkInterfaces" + }, + { + "apiVersion": "[variables('apiVersionDefault')]", + "copy": { + "count": "[variables('masterCount')]", + "name": "vmLoopNode" + }, + "dependsOn": [ + "[concat('Microsoft.Network/networkInterfaces/', variables('masterVMNamePrefix'), 'nic-', copyIndex())]", + "[concat('Microsoft.Compute/availabilitySets/',variables('masterAvailabilitySet'))]", + "[variables('masterStorageAccountName')]", + "[variables('masterStorageAccountExhibitorName')]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(variables('masterVMNamePrefix'), copyIndex())]", + "properties": { + "availabilitySet": { + "id": "[resourceId('Microsoft.Compute/availabilitySets',variables('masterAvailabilitySet'))]" + }, + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": "true", + "storageUri": "[reference(concat('Microsoft.Storage/storageAccounts/', variables('masterStorageAccountName')), variables('apiVersionStorage')).primaryEndpoints.blob]" + } + }, + "hardwareProfile": { + "vmSize": "[variables('masterVMSize')]" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces',concat(variables('masterVMNamePrefix'), 'nic-', copyIndex()))]" + } + ] + }, + "osProfile": { + "adminUsername": "[variables('adminUsername')]", + "computername": "[concat(variables('masterVMNamePrefix'), copyIndex())]", + "customData": "[base64(concat({{if IsDCOS173}}{{template "dcoscustomdata173.t" dict "DCOSCustomDataPublicIPStr" GetDCOSCustomDataPublicIPStr "DCOSGUID" GetDCOSGUID "RolesString" GetMasterRolesFileContents}}{{else if IsDCOS184}}{{template "dcoscustomdata184.t" dict "DCOSCustomDataPublicIPStr" GetDCOSCustomDataPublicIPStr "DCOSGUID" GetDCOSGUID "RolesString" GetMasterRolesFileContents}}{{end}}))]", + "linuxConfiguration": { + "disablePasswordAuthentication": "true", + "ssh": { + "publicKeys": [ + { + "keyData": "[variables('sshRSAPublicKey')]", + "path": "[variables('sshKeyPath')]" + } + ] + } + } + }, + "storageProfile": { + "imageReference": { + "offer": "[variables('osImageOffer')]", + "publisher": "[variables('osImagePublisher')]", + "sku": "[variables('osImageSKU')]", + "version": "[variables('osImageVersion')]" + }, + "osDisk": { + "caching": "ReadWrite", + "createOption": "FromImage", + "name": "[concat(variables('masterVMNamePrefix'), copyIndex(),'-osdisk')]", + "vhd": { + "uri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/',variables('masterStorageAccountName')),variables('apiVersionStorage')).primaryEndpoints.blob,'vhds/',variables('masterVMNamePrefix'),copyIndex(),'-osdisk.vhd')]" + } + } + } + }, + "type": "Microsoft.Compute/virtualMachines" + }, + { + "apiVersion": "[variables('apiVersionDefault')]", + "dependsOn": [ + "[concat('Microsoft.Compute/virtualMachines/', variables('masterVMNamePrefix'), sub(variables('masterCount'), 1))]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(variables('masterVMNamePrefix'), sub(variables('masterCount'), 1), '/waitforleader')]", + "properties": { + "autoUpgradeMinorVersion": true, + "publisher": "Microsoft.OSTCExtensions", + "settings": { + "commandToExecute": "sh -c 'until ping -c1 leader.mesos;do echo waiting for leader.mesos;sleep 15;done;echo leader.mesos up'" + }, + "type": "CustomScriptForLinux", + "typeHandlerVersion": "1.4" + }, + "type": "Microsoft.Compute/virtualMachines/extensions" + } \ No newline at end of file diff --git a/acstgen/parts/dcosmastervars.t b/acstgen/parts/dcosmastervars.t new file mode 100644 index 0000000000..ca105614fc --- /dev/null +++ b/acstgen/parts/dcosmastervars.t @@ -0,0 +1,237 @@ + "adminUsername": "[parameters('linuxAdminUsername')]", + "maxVMsPerPool": 100, + "maxVMsPerStorageAccount": 20, + "maxStorageAccountsPerAgent": "[div(variables('maxVMsPerPool'),variables('maxVMsPerStorageAccount'))]", + "dataStorageAccountPrefixSeed": 97, + "apiVersionDefault": "2016-03-30", + "apiVersionStorage": "2015-06-15", + "masterAvailabilitySet": "[concat(variables('orchestratorName'), '-master-availabilitySet-', variables('nameSuffix'))]", + "masterCount": {{.MasterProfile.Count}}, + "masterEndpointDNSNamePrefix": "[tolower(parameters('masterEndpointDNSNamePrefix'))]", + "masterLbBackendPoolName": "[concat(variables('orchestratorName'), '-master-pool-', variables('nameSuffix'))]", + "masterLbID": "[resourceId('Microsoft.Network/loadBalancers',variables('masterLbName'))]", + "masterLbIPConfigID": "[concat(variables('masterLbID'),'/frontendIPConfigurations/', variables('masterLbIPConfigName'))]", + "masterLbIPConfigName": "[concat(variables('orchestratorName'), '-master-lbFrontEnd-', variables('nameSuffix'))]", + "masterLbName": "[concat(variables('orchestratorName'), '-master-lb-', variables('nameSuffix'))]", + "masterNSGID": "[resourceId('Microsoft.Network/networkSecurityGroups',variables('masterNSGName'))]", + "masterNSGName": "[concat(variables('orchestratorName'), '-master-nsg-', variables('nameSuffix'))]", + "masterPublicIPAddressName": "[concat(variables('orchestratorName'), '-master-ip-', variables('masterEndpointDNSNamePrefix'), '-', variables('nameSuffix'))]", + "masterStorageAccountExhibitorName": "[concat(variables('storageAccountBaseName'), 'exhb0')]", + "masterStorageAccountName": "[concat(variables('storageAccountBaseName'), 'mstr0')]", +{{if .MasterProfile.IsCustomVNET}} + "masterVnetSubnetID": "[parameters('masterVnetSubnetID')]", +{{else}} + "masterSubnet": "[parameters('masterSubnet')]", + "masterSubnetName": "[concat(variables('orchestratorName'), '-masterSubnet')]", + "vnetID": "[resourceId('Microsoft.Network/virtualNetworks',variables('virtualNetworkName'))]", + "masterVnetSubnetID": "[concat(variables('vnetID'),'/subnets/',variables('masterSubnetName'))]", + "virtualNetworkName": "[concat(variables('orchestratorName'), '-vnet-', variables('nameSuffix'))]", +{{end}} + "masterFirstAddrOctets": "[split(parameters('firstConsecutiveStaticIP'),'.')]", + "masterFirstAddrOctet4": "[variables('masterFirstAddrOctets')[3]]", + "masterFirstAddrPrefix": "[concat(variables('masterFirstAddrOctets')[0],'.',variables('masterFirstAddrOctets')[1],'.',variables('masterFirstAddrOctets')[2],'.')]", + "masterVMNamePrefix": "[concat(variables('orchestratorName'), '-master-', variables('nameSuffix'), '-')]", + "masterVMNic": [ + "[concat(variables('masterVMNamePrefix'), 'nic-0')]", + "[concat(variables('masterVMNamePrefix'), 'nic-1')]", + "[concat(variables('masterVMNamePrefix'), 'nic-2')]", + "[concat(variables('masterVMNamePrefix'), 'nic-3')]", + "[concat(variables('masterVMNamePrefix'), 'nic-4')]", + "[concat(variables('masterVMNamePrefix'), 'nic-5')]", + "[concat(variables('masterVMNamePrefix'), 'nic-6')]" + ], + "masterVMSize": "[parameters('masterVMSize')]", + "nameSuffix": "{{GetUniqueNameSuffix}}", + "oauthEnabled": "false", + "orchestratorName": "dcos", + "osImageOffer": "UbuntuServer", + "osImagePublisher": "Canonical", + "osImageSKU": "16.04.0-LTS", + "osImageVersion": "16.04.201606270", + "sshKeyPath": "[concat('/home/', variables('adminUsername'), '/.ssh/authorized_keys')]", + "sshRSAPublicKey": "[parameters('sshRSAPublicKey')]", + "storageAccountBaseName": "[uniqueString(concat(variables('masterEndpointDNSNamePrefix'),resourceGroup().location, variables('orchestratorName')))]", + "storageAccountPrefixes": [ + "0", + "6", + "c", + "i", + "o", + "u", + "1", + "7", + "d", + "j", + "p", + "v", + "2", + "8", + "e", + "k", + "q", + "w", + "3", + "9", + "f", + "l", + "r", + "x", + "4", + "a", + "g", + "m", + "s", + "y", + "5", + "b", + "h", + "n", + "t", + "z" + ], + "storageAccountPrefixesCount": "[length(variables('storageAccountPrefixes'))]", + "storageAccountType": "Standard_LRS", + "storageLocation": "[resourceGroup().location]", + "vmSizesMap": { + "Standard_A0": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A1": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A10": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A11": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A3": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A4": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A5": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A6": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A7": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A8": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A9": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D1": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D11": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D11_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D12": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D12_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D13": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D13_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D14": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D14_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D1_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D2_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D3": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D3_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D4": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D4_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D5_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_DS1": { + "storageAccountType": "Premium_LRS" + }, + "Standard_DS11": { + "storageAccountType": "Premium_LRS" + }, + "Standard_DS12": { + "storageAccountType": "Premium_LRS" + }, + "Standard_DS13": { + "storageAccountType": "Premium_LRS" + }, + "Standard_DS14": { + "storageAccountType": "Premium_LRS" + }, + "Standard_DS2": { + "storageAccountType": "Premium_LRS" + }, + "Standard_DS3": { + "storageAccountType": "Premium_LRS" + }, + "Standard_DS4": { + "storageAccountType": "Premium_LRS" + }, + "Standard_G1": { + "storageAccountType": "Standard_LRS" + }, + "Standard_G2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_G3": { + "storageAccountType": "Standard_LRS" + }, + "Standard_G4": { + "storageAccountType": "Standard_LRS" + }, + "Standard_G5": { + "storageAccountType": "Standard_LRS" + }, + "Standard_GS1": { + "storageAccountType": "Premium_LRS" + }, + "Standard_GS2": { + "storageAccountType": "Premium_LRS" + }, + "Standard_GS3": { + "storageAccountType": "Premium_LRS" + }, + "Standard_GS4": { + "storageAccountType": "Premium_LRS" + }, + "Standard_GS5": { + "storageAccountType": "Premium_LRS" + } + } diff --git a/acstgen/parts/masteroutputs.t b/acstgen/parts/masteroutputs.t new file mode 100644 index 0000000000..1d12039f57 --- /dev/null +++ b/acstgen/parts/masteroutputs.t @@ -0,0 +1,4 @@ + "masterFQDN": { + "type": "string", + "value": "[reference(concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))).dnsSettings.fqdn]" + } diff --git a/acstgen/parts/masterparams.t b/acstgen/parts/masterparams.t new file mode 100644 index 0000000000..0a14ec5e4a --- /dev/null +++ b/acstgen/parts/masterparams.t @@ -0,0 +1,101 @@ + "linuxAdminUsername": { + "defaultValue": "{{.LinuxProfile.AdminUsername}}", + "metadata": { + "description": "User name for the Linux Virtual Machines (SSH or Password)." + }, + "type": "string" + }, + "masterEndpointDNSNamePrefix": { + "defaultValue": "{{.MasterProfile.DNSPrefix}}", + "metadata": { + "description": "Sets the Domain name label for the master IP Address. The concatenation of the domain name label and the regional DNS zone make up the fully qualified domain name associated with the public IP address." + }, + "type": "string" + }, +{{if .MasterProfile.IsCustomVNET}} + "masterVnetSubnetID": { + "defaultValue": "{{.MasterProfile.VnetSubnetID}}", + "metadata": { + "description": "Sets the vnet subnet of the master." + }, + "type": "string" + }, +{{else}} + "masterSubnet": { + "defaultValue": "{{.MasterProfile.GetSubnet}}", + "metadata": { + "description": "Sets the subnet of the master." + }, + "type": "string" + }, +{{end}} + "firstConsecutiveStaticIP": { + "defaultValue": "{{.MasterProfile.FirstConsecutiveStaticIP}}", + "metadata": { + "description": "Sets the static IP of the first master" + }, + "type": "string" + }, + "masterVMSize": { + "allowedValues": [ + "Standard_A0", + "Standard_A1", + "Standard_A2", + "Standard_A3", + "Standard_A4", + "Standard_A5", + "Standard_A6", + "Standard_A7", + "Standard_A8", + "Standard_A9", + "Standard_A10", + "Standard_A11", + "Standard_D1", + "Standard_D2", + "Standard_D3", + "Standard_D4", + "Standard_D11", + "Standard_D12", + "Standard_D13", + "Standard_D14", + "Standard_D1_v2", + "Standard_D2_v2", + "Standard_D3_v2", + "Standard_D4_v2", + "Standard_D5_v2", + "Standard_D11_v2", + "Standard_D12_v2", + "Standard_D13_v2", + "Standard_D14_v2", + "Standard_G1", + "Standard_G2", + "Standard_G3", + "Standard_G4", + "Standard_G5", + "Standard_DS1", + "Standard_DS2", + "Standard_DS3", + "Standard_DS4", + "Standard_DS11", + "Standard_DS12", + "Standard_DS13", + "Standard_DS14", + "Standard_GS1", + "Standard_GS2", + "Standard_GS3", + "Standard_GS4", + "Standard_GS5" + ], + "defaultValue": "{{.MasterProfile.VMSize}}", + "metadata": { + "description": "The size of the Virtual Machine." + }, + "type": "string" + }, + "sshRSAPublicKey": { + "defaultValue": "{{GetLinuxProfileFirstSSHPublicKey}}", + "metadata": { + "description": "SSH public key used for auth to all Linux machines. Not Required. If not set, you must provide a password key." + }, + "type": "string" + } \ No newline at end of file diff --git a/acstgen/parts/swarmagentcustomdata.t b/acstgen/parts/swarmagentcustomdata.t new file mode 100644 index 0000000000..4d355cbd26 --- /dev/null +++ b/acstgen/parts/swarmagentcustomdata.t @@ -0,0 +1 @@ +concat('#cloud-config\n\nwrite_files:\n - encoding: gzip\n content: !!binary |\n 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\n path: /opt/azure/containers/configure-swarm-cluster.sh\n permissions: \"0744\"\n\n',variables('agentRunCmdFile'),variables('agentRunCmd')) \ No newline at end of file diff --git a/acstgen/parts/swarmagentresources.t b/acstgen/parts/swarmagentresources.t new file mode 100644 index 0000000000..35e8e82d7c --- /dev/null +++ b/acstgen/parts/swarmagentresources.t @@ -0,0 +1,155 @@ + { + "apiVersion": "[variables('storageApiVersion')]", + "copy": { + "count": "[variables('agentStorageAccountsCount')]", + "name": "vmLoopNode" + }, + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(variables('storageAccountPrefixes')[mod(add(copyIndex(),variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(copyIndex(),variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName'))]", + "properties": { + "accountType": "[variables('vmSizesMap')[variables('{{.Name}}VMSize')].storageAccountType]" + }, + "type": "Microsoft.Storage/storageAccounts" + }, +{{if IsPublic .Ports}} + { + "apiVersion": "[variables('networkApiVersion')]", + "location": "[resourceGroup().location]", + "name": "[variables('{{.Name}}IPAddressName')]", + "properties": { + "dnsSettings": { + "domainNameLabel": "[variables('{{.Name}}EndpointDNSNamePrefix')]" + }, + "publicIPAllocationMethod": "Dynamic" + }, + "type": "Microsoft.Network/publicIPAddresses" + }, + { + "apiVersion": "[variables('networkApiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('{{.Name}}IPAddressName'))]" + ], + "location": "[resourceGroup().location]", + "name": "[variables('{{.Name}}LbName')]", + "properties": { + "backendAddressPools": [ + { + "name": "[variables('{{.Name}}LbBackendPoolName')]" + } + ], + "frontendIPConfigurations": [ + { + "name": "[variables('{{.Name}}LbIPConfigName')]", + "properties": { + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('{{.Name}}IPAddressName'))]" + } + } + } + ], + "inboundNatRules": [], + "loadBalancingRules": [ + {{(GetLBRules .Name .Ports)}} + ], + "probes": [ + {{(GetProbes .Ports)}} + ] + }, + "type": "Microsoft.Network/loadBalancers" + }, +{{end}} + { + "apiVersion": "[variables('computeApiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Storage/storageAccounts/', variables('storageAccountPrefixes')[mod(add(0,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(0,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName'))]", + "[concat('Microsoft.Storage/storageAccounts/', variables('storageAccountPrefixes')[mod(add(1,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(1,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName'))]", + "[concat('Microsoft.Storage/storageAccounts/', variables('storageAccountPrefixes')[mod(add(2,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(2,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName'))]", + "[concat('Microsoft.Storage/storageAccounts/', variables('storageAccountPrefixes')[mod(add(3,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(3,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName'))]", + "[concat('Microsoft.Storage/storageAccounts/', variables('storageAccountPrefixes')[mod(add(4,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(4,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName'))]", + "[variables('vnetID')]" +{{if IsPublic .Ports}}, + "[variables('{{.Name}}LbID')]" +{{end}} + ], + "location": "[resourceGroup().location]", + "name": "[concat(variables('{{.Name}}VMNamePrefix'), '-vmss')]", + "properties": { + "upgradePolicy": { + "mode": "Automatic" + }, + "virtualMachineProfile": { + "networkProfile": { + "networkInterfaceConfigurations": [ + { + "name": "nic", + "properties": { + "ipConfigurations": [ + { + "name": "nicipconfig", + "properties": { +{{if IsPublic .Ports}} + "loadBalancerBackendAddressPools": [ + { + "id": "[concat(variables('{{.Name}}LbID'), '/backendAddressPools/', variables('{{.Name}}LbBackendPoolName'))]" + } + ], +{{end}} + "subnet": { + "id": "[variables('{{.Name}}SubnetRef')]" + } + } + } + ], + "primary": "true" + } + } + ] + }, + "osProfile": { + "adminUsername": "[variables('adminUsername')]", + "computerNamePrefix": "[variables('{{.Name}}VMNamePrefix')]", + "customData": "[base64({{template "swarmagentcustomdata.t" .}})]", + "linuxConfiguration": { + "disablePasswordAuthentication": "true", + "ssh": { + "publicKeys": [ + { + "keyData": "[parameters('sshRSAPublicKey')]", + "path": "[variables('sshKeyPath')]" + } + ] + } + } + }, + "storageProfile": { + "imageReference": { + "offer": "[variables('osImageOffer')]", + "publisher": "[variables('osImagePublisher')]", + "sku": "[variables('osImageSKU')]", + "version": "latest" + }, + "osDisk": { + "caching": "ReadWrite", + "createOption": "FromImage", + "name": "vmssosdisk", + "vhdContainers": [ + "[concat(reference(concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(0,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(0,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName')), variables('storageApiVersion') ).primaryEndpoints.blob, 'osdisk')]", + "[concat(reference(concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(1,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(1,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName')), variables('storageApiVersion')).primaryEndpoints.blob, 'osdisk')]", + "[concat(reference(concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(2,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(2,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName')), variables('storageApiVersion')).primaryEndpoints.blob, 'osdisk')]", + "[concat(reference(concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(3,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(3,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName')), variables('storageApiVersion')).primaryEndpoints.blob, 'osdisk')]", + "[concat(reference(concat('Microsoft.Storage/storageAccounts/',variables('storageAccountPrefixes')[mod(add(4,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('storageAccountPrefixes')[div(add(4,variables('{{.Name}}StorageAccountOffset')),variables('storageAccountPrefixesCount'))],variables('{{.Name}}AccountName')), variables('storageApiVersion')).primaryEndpoints.blob, 'osdisk')]" + ] + } + } + } + }, + "sku": { + "capacity": "[variables('{{.Name}}Count')]", + "name": "[variables('{{.Name}}VMSize')]", + "tier": "Standard" + }, + "type": "Microsoft.Compute/virtualMachineScaleSets" + } diff --git a/acstgen/parts/swarmagentvars.t b/acstgen/parts/swarmagentvars.t new file mode 100644 index 0000000000..3f7ba873fb --- /dev/null +++ b/acstgen/parts/swarmagentvars.t @@ -0,0 +1,16 @@ + "{{.Name}}StorageAccountOffset": "[mul(variables('agentStorageAccountsCount'),variables('{{.Name}}Index'))]", + "{{.Name}}Count": "[parameters('{{.Name}}Count')]", + "{{.Name}}VMNamePrefix": "[concat(variables('orchestratorName'), '-{{.Name}}-', variables('nameSuffix'))]", + "{{.Name}}VMSize": "[parameters('{{.Name}}VMSize')]", + "{{.Name}}Subnet": "[parameters('{{.Name}}Subnet')]", + "{{.Name}}SubnetName": "[concat(variables('orchestratorName'), '-{{.Name}}subnet')]", + "{{.Name}}SubnetRef": "[concat(variables('vnetID'),'/subnets/',variables('{{.Name}}SubnetName'))]", +{{if IsPublic .Ports}} + "{{.Name}}EndpointDNSNamePrefix": "[tolower(parameters('{{.Name}}EndpointDNSNamePrefix'))]", + "{{.Name}}IPAddressName": "[concat(variables('orchestratorName'), '-agent-ip-', variables('{{.Name}}EndpointDNSNamePrefix'), '-', variables('nameSuffix'))]", + "{{.Name}}LbBackendPoolName": "[concat(variables('orchestratorName'), '-{{.Name}}-', variables('nameSuffix'))]", + "{{.Name}}LbID": "[resourceId('Microsoft.Network/loadBalancers',variables('{{.Name}}LbName'))]", + "{{.Name}}LbIPConfigID": "[concat(variables('{{.Name}}LbID'),'/frontendIPConfigurations/', variables('{{.Name}}LbIPConfigName'))]", + "{{.Name}}LbIPConfigName": "[concat(variables('orchestratorName'), '-{{.Name}}-', variables('nameSuffix'))]", + "{{.Name}}LbName": "[concat(variables('orchestratorName'), '-{{.Name}}-', variables('nameSuffix'))]", + {{end}} diff --git a/acstgen/parts/swarmbase.t b/acstgen/parts/swarmbase.t new file mode 100644 index 0000000000..c1c1a240f7 --- /dev/null +++ b/acstgen/parts/swarmbase.t @@ -0,0 +1,25 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + {{range .AgentPoolProfiles}}{{template "agentparams.t" .}},{{end}} + {{template "masterparams.t" .}} + }, + "variables": { + {{range $index, $agent := .AgentPoolProfiles}} + {{template "swarmagentvars.t" .}} + "{{.Name}}Index": {{$index}}, + "{{.Name}}AccountName": "[concat(variables('storageAccountBaseName'), 'agnt{{$index}}')]", + {{end}} + + {{template "swarmmastervars.t" .}} + }, + "resources": [ + {{range .AgentPoolProfiles}}{{template "swarmagentresources.t" .}},{{end}} + {{template "swarmmasterresources.t" .}} + ], + "outputs": { + {{range .AgentPoolProfiles}}{{template "agentoutputs.t" .}} + {{end}}{{template "masteroutputs.t" .}} + } +} diff --git a/acstgen/parts/swarmmastercustomdata.t b/acstgen/parts/swarmmastercustomdata.t new file mode 100644 index 0000000000..26471c4cb8 --- /dev/null +++ b/acstgen/parts/swarmmastercustomdata.t @@ -0,0 +1 @@ +'#cloud-config\n\nwrite_files:\n - encoding: gzip\n content: !!binary |\n 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\n path: /opt/azure/containers/configure-swarm-cluster.sh\n permissions: \"0744\"\n\n' \ No newline at end of file diff --git a/acstgen/parts/swarmmasterresources.t b/acstgen/parts/swarmmasterresources.t new file mode 100644 index 0000000000..c43be565a0 --- /dev/null +++ b/acstgen/parts/swarmmasterresources.t @@ -0,0 +1,218 @@ + { + "apiVersion": "[variables('storageApiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]" + ], + "location": "[resourceGroup().location]", + "name": "[variables('masterStorageAccountName')]", + "properties": { + "accountType": "[variables('vmSizesMap')[variables('masterVMSize')].storageAccountType]" + }, + "type": "Microsoft.Storage/storageAccounts" + }, + { + "apiVersion": "[variables('networkApiVersion')]", + "location": "[resourceGroup().location]", + "name": "[variables('virtualNetworkName')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + {{GetVNETAddressPrefixes}} + ] + }, + "subnets": [ + {{GetVNETSubnets false}} + ] + }, + "type": "Microsoft.Network/virtualNetworks" + }, + { + "apiVersion": "[variables('computeApiVersion')]", + "location": "[resourceGroup().location]", + "name": "[variables('masterAvailabilitySet')]", + "properties": {}, + "type": "Microsoft.Compute/availabilitySets" + }, + { + "apiVersion": "[variables('networkApiVersion')]", + "location": "[resourceGroup().location]", + "name": "[variables('masterPublicIPAddressName')]", + "properties": { + "dnsSettings": { + "domainNameLabel": "[variables('masterEndpointDNSNamePrefix')]" + }, + "publicIPAllocationMethod": "Dynamic" + }, + "type": "Microsoft.Network/publicIPAddresses" + }, + { + "apiVersion": "[variables('networkApiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]" + ], + "location": "[resourceGroup().location]", + "name": "[variables('masterLbName')]", + "properties": { + "backendAddressPools": [ + { + "name": "[variables('masterLbBackendPoolName')]" + } + ], + "frontendIPConfigurations": [ + { + "name": "[variables('masterLbIPConfigName')]", + "properties": { + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('masterPublicIPAddressName'))]" + } + } + } + ] + }, + "type": "Microsoft.Network/loadBalancers" + }, + { + "apiVersion": "[variables('networkApiVersion')]", + "copy": { + "count": "[variables('masterCount')]", + "name": "masterLbLoopNode" + }, + "dependsOn": [ + "[variables('masterLbID')]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(variables('masterLbName'), '/', 'SSH-', variables('masterVMNamePrefix'), copyIndex())]", + "properties": { + "backendPort": 22, + "enableFloatingIP": false, + "frontendIPConfiguration": { + "id": "[variables('masterLbIPConfigID')]" + }, + "frontendPort": "[copyIndex(2200)]", + "protocol": "tcp" + }, + "type": "Microsoft.Network/loadBalancers/inboundNatRules" + }, + { + "apiVersion": "[variables('networkApiVersion')]", + "copy": { + "count": "[variables('masterCount')]", + "name": "nicLoopNode" + }, + "dependsOn": [ + "[variables('masterLbID')]", + "[variables('vnetID')]", + "[concat(variables('masterLbID'),'/inboundNatRules/SSH-',variables('masterVMNamePrefix'),copyIndex())]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(variables('masterVMNamePrefix'), 'nic-', copyIndex())]", + "properties": { + "ipConfigurations": [ + { + "name": "ipConfigNode", + "properties": { + "loadBalancerBackendAddressPools": [ + { + "id": "[concat(variables('masterLbID'), '/backendAddressPools/', variables('masterLbBackendPoolName'))]" + } + ], + "loadBalancerInboundNatRules": [ + { + "id": "[concat(variables('masterLbID'),'/inboundNatRules/SSH-',variables('masterVMNamePrefix'),copyIndex())]" + } + ], + "privateIPAddress": "[concat(variables('masterFirstAddrPrefix'), copyIndex(int(variables('masterFirstAddrOctet4'))))]", + "privateIPAllocationMethod": "Static", + "subnet": { + "id": "[variables('masterSubnetRef')]" + } + } + } + ] + }, + "type": "Microsoft.Network/networkInterfaces" + }, + { + "apiVersion": "[variables('computeApiVersion')]", + "copy": { + "count": "[variables('masterCount')]", + "name": "vmLoopNode" + }, + "dependsOn": [ + "[concat('Microsoft.Network/networkInterfaces/', variables('masterVMNamePrefix'), 'nic-', copyIndex())]", + "[concat('Microsoft.Compute/availabilitySets/',variables('masterAvailabilitySet'))]", + "[variables('masterStorageAccountName')]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(variables('masterVMNamePrefix'), copyIndex())]", + "properties": { + "availabilitySet": { + "id": "[resourceId('Microsoft.Compute/availabilitySets',variables('masterAvailabilitySet'))]" + }, + "hardwareProfile": { + "vmSize": "[variables('masterVMSize')]" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces',concat(variables('masterVMNamePrefix'), 'nic-', copyIndex()))]" + } + ] + }, + "osProfile": { + "adminUsername": "[variables('adminUsername')]", + "computername": "[concat(variables('masterVMNamePrefix'), copyIndex())]", + "customData": "[base64({{template "swarmmastercustomdata.t" .}})]", + "linuxConfiguration": { + "disablePasswordAuthentication": "true", + "ssh": { + "publicKeys": [ + { + "keyData": "[variables('sshRSAPublicKey')]", + "path": "[variables('sshKeyPath')]" + } + ] + } + } + }, + "storageProfile": { + "imageReference": { + "offer": "[variables('osImageOffer')]", + "publisher": "[variables('osImagePublisher')]", + "sku": "[variables('osImageSKU')]", + "version": "[variables('osImageVersion')]" + }, + "osDisk": { + "caching": "ReadWrite", + "createOption": "FromImage", + "name": "[concat(variables('masterVMNamePrefix'), copyIndex(),'-osdisk')]", + "vhd": { + "uri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/', variables('masterStorageAccountName')), variables('storageApiVersion')).primaryEndpoints.blob, 'vhds/', variables('masterVMNamePrefix'), copyIndex(), '-osdisk.vhd')]" + } + } + } + }, + "type": "Microsoft.Compute/virtualMachines" + }, + { + "apiVersion": "[variables('computeApiVersion')]", + "copy": { + "count": "[variables('masterCount')]", + "name": "vmLoopNode" + }, + "dependsOn": [ + "[concat('Microsoft.Compute/virtualMachines/', concat(variables('masterVMNamePrefix'), copyIndex()))]" + ], + "location": "[resourceGroup().location]", + "name": "[concat(variables('masterVMNamePrefix'), copyIndex(), '/configuremaster')]", + "properties": { + "publisher": "Microsoft.OSTCExtensions", + "settings": { + "commandToExecute": "[variables('masterCustomScript')]", + "fileUris": [] + }, + "type": "CustomScriptForLinux", + "typeHandlerVersion": "1.4" + }, + "type": "Microsoft.Compute/virtualMachines/extensions" + } \ No newline at end of file diff --git a/acstgen/parts/swarmmastervars.t b/acstgen/parts/swarmmastervars.t new file mode 100644 index 0000000000..3d2ed16bbd --- /dev/null +++ b/acstgen/parts/swarmmastervars.t @@ -0,0 +1,225 @@ + "adminUsername": "[parameters('linuxAdminUsername')]", + "agentStorageAccountsCount": 5, + "agentCustomScript": "[concat('/usr/bin/nohup /bin/bash -c \"/bin/bash /opt/azure/containers/configure-swarm-cluster.sh ',variables('clusterInstallParameters'),' >> /var/log/azure/cluster-bootstrap.log 2>&1 &\" &')]", + "agentRunCmd": "[concat('runcmd:\n - [ /bin/bash, /opt/azure/containers/install-cluster.sh ]\n\n')]", + "agentRunCmdFile": "[concat(' - content: |\n #!/bin/bash\n ',variables('agentCustomScript'),'\n path: /opt/azure/containers/install-cluster.sh\n permissions: \"0744\"\n')]", + "clusterInstallParameters": "[concat(variables('masterCount'), ' ',variables('masterVMNamePrefix'), ' ',variables('masterFirstAddr'), ' ',variables('adminUsername'),' ',variables('postInstallScriptURI'),' ',split(variables('masterSubnet'),'0/24')[0])]", + "computeApiVersion": "2016-03-30", + "masterSubnet": "[parameters('masterSubnet')]", + "masterAvailabilitySet": "[concat(variables('orchestratorName'), '-master-availabilitySet-', variables('nameSuffix'))]", + "masterCount": {{.MasterProfile.Count}}, + "masterCustomScript": "[concat('/bin/bash -c \"/bin/bash /opt/azure/containers/configure-swarm-cluster.sh ',variables('clusterInstallParameters'),' >> /var/log/azure/cluster-bootstrap.log 2>&1\"')]", + "masterEndpointDNSNamePrefix": "[tolower(parameters('masterEndpointDNSNamePrefix'))]", + "masterFirstAddr": 5, + "masterLbBackendPoolName": "[concat(variables('orchestratorName'), '-master-pool-', variables('nameSuffix'))]", + "masterLbID": "[resourceId('Microsoft.Network/loadBalancers',variables('masterLbName'))]", + "masterLbIPConfigID": "[concat(variables('masterLbID'),'/frontendIPConfigurations/', variables('masterLbIPConfigName'))]", + "masterLbIPConfigName": "[concat(variables('orchestratorName'), '-master-lbFrontEnd-', variables('nameSuffix'))]", + "masterLbName": "[concat(variables('orchestratorName'), '-master-lb-', variables('nameSuffix'))]", + "masterPublicIPAddressName": "[concat(variables('orchestratorName'), '-master-ip-', variables('masterEndpointDNSNamePrefix'), '-', variables('nameSuffix'))]", + "masterStorageAccountName": "[concat(variables('storageAccountBaseName'), '0')]", + "masterSubnetName": "[concat(variables('orchestratorName'), '-masterSubnet')]", + "masterSubnetRef": "[concat(variables('vnetID'),'/subnets/',variables('masterSubnetName'))]", + "masterFirstAddrOctets": "[split(parameters('firstConsecutiveStaticIP'),'.')]", + "masterFirstAddrOctet4": "[variables('masterFirstAddrOctets')[3]]", + "masterFirstAddrPrefix": "[concat(variables('masterFirstAddrOctets')[0],'.',variables('masterFirstAddrOctets')[1],'.',variables('masterFirstAddrOctets')[2],'.')]", + "masterVMNamePrefix": "[concat(variables('orchestratorName'), '-master-', variables('nameSuffix'), '-')]", + "masterVMSize": "[parameters('masterVMSize')]", + "nameSuffix": "{{GetUniqueNameSuffix}}", + "networkApiVersion": "2016-03-30", + "orchestratorName": "swarm", + "osImageOffer": "UbuntuServer", + "osImagePublisher": "Canonical", + "osImageSKU": "14.04.4-LTS", + "osImageVersion": "latest", + "postInstallScriptURI": "disabled", + "sshKeyPath": "[concat('/home/', variables('adminUsername'), '/.ssh/authorized_keys')]", + "sshRSAPublicKey": "[parameters('sshRSAPublicKey')]", + "storageAccountBaseName": "[concat(uniqueString(concat(variables('masterEndpointDNSNamePrefix'),resourceGroup().location)))]", + "storageAccountPrefixes": [ + "0", + "6", + "c", + "i", + "o", + "u", + "1", + "7", + "d", + "j", + "p", + "v", + "2", + "8", + "e", + "k", + "q", + "w", + "3", + "9", + "f", + "l", + "r", + "x", + "4", + "a", + "g", + "m", + "s", + "y", + "5", + "b", + "h", + "n", + "t", + "z" + ], + "storageAccountPrefixesCount": "[length(variables('storageAccountPrefixes'))]", + "storageApiVersion": "2015-06-15", + "virtualNetworkName": "[concat(variables('orchestratorName'), '-vnet-', variables('nameSuffix'))]", + "vmSizesMap": { + "Standard_A0": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A1": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A10": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A11": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A3": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A4": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A5": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A6": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A7": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A8": { + "storageAccountType": "Standard_LRS" + }, + "Standard_A9": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D1": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D11": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D11_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D12": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D12_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D13": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D13_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D14": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D14_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D1_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D2_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D3": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D3_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D4": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D4_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_D5_v2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_DS1": { + "storageAccountType": "Premium_LRS" + }, + "Standard_DS11": { + "storageAccountType": "Premium_LRS" + }, + "Standard_DS12": { + "storageAccountType": "Premium_LRS" + }, + "Standard_DS13": { + "storageAccountType": "Premium_LRS" + }, + "Standard_DS14": { + "storageAccountType": "Premium_LRS" + }, + "Standard_DS2": { + "storageAccountType": "Premium_LRS" + }, + "Standard_DS3": { + "storageAccountType": "Premium_LRS" + }, + "Standard_DS4": { + "storageAccountType": "Premium_LRS" + }, + "Standard_G1": { + "storageAccountType": "Standard_LRS" + }, + "Standard_G2": { + "storageAccountType": "Standard_LRS" + }, + "Standard_G3": { + "storageAccountType": "Standard_LRS" + }, + "Standard_G4": { + "storageAccountType": "Standard_LRS" + }, + "Standard_G5": { + "storageAccountType": "Standard_LRS" + }, + "Standard_GS1": { + "storageAccountType": "Premium_LRS" + }, + "Standard_GS2": { + "storageAccountType": "Premium_LRS" + }, + "Standard_GS3": { + "storageAccountType": "Premium_LRS" + }, + "Standard_GS4": { + "storageAccountType": "Premium_LRS" + }, + "Standard_GS5": { + "storageAccountType": "Premium_LRS" + } + }, + "vmsPerStorageAccount": 20, + "vnetID": "[resourceId('Microsoft.Network/virtualNetworks',variables('virtualNetworkName'))]" + \ No newline at end of file