Merge pull request #4 from Azure/anhowe-kubernetes

Add Kubernetes, VNET support for all orchestrators, volume support for all orchestrators, windows support in swarm
This commit is contained in:
anhowe 2016-10-11 08:43:45 -07:00 коммит произвёл GitHub
Родитель 71cf145bcd 85043f81e0
Коммит a210a4f714
68 изменённых файлов: 4798 добавлений и 1261 удалений

5
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,5 @@
*.anthony.json
deployVM0.ps1
noparams.json
output*
acstgen.exe

Просмотреть файл

@ -6,10 +6,11 @@ import (
"fmt"
"io/ioutil"
"os"
"path"
"strings"
"./api/vlabs"
"./clustertemplate"
"./tgen"
)
// loadAcsCluster loads an ACS Cluster API Model from a JSON file
@ -23,7 +24,7 @@ func loadAcsCluster(jsonFile string) (*vlabs.AcsCluster, error) {
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())
}
@ -61,10 +62,14 @@ func prettyPrintJSON(content string) (string, error) {
func prettyPrintArmTemplate(template string) (string, error) {
translateParams := [][]string{
{"parameters", "dparameters"},
{"variables", "evariables"},
{"resources", "fresources"},
{"outputs", "zoutputs"},
{"\"parameters\"", "\"dparameters\""},
{"\"variables\"", "\"evariables\""},
{"\"resources\"", "\"fresources\""},
{"\"outputs\"", "\"zoutputs\""},
// there is a bug in ARM where it doesn't correctly translate back '\u003e' (>)
{">", "GREATERTHAN"},
{"<", "LESSTHAN"},
{"&", "AMPERSAND"},
}
template = translateJSON(template, translateParams, false)
@ -77,11 +82,98 @@ func prettyPrintArmTemplate(template string) (string, error) {
return template, nil
}
func writeArtifacts(acsCluster *vlabs.AcsCluster, template string, parameters, artifactsDir string, templateDirectory string, certsGenerated bool) error {
if len(artifactsDir) == 0 {
artifactsDir = fmt.Sprintf("%s-%s", acsCluster.OrchestratorProfile.OrchestratorType, tgen.GenerateClusterID(acsCluster))
artifactsDir = path.Join("output", artifactsDir)
}
b, err := json.MarshalIndent(acsCluster, "", " ")
if err != nil {
return err
}
if e := saveFile(artifactsDir, "apimodel.json", b); e != nil {
return e
}
if e := saveFileString(artifactsDir, "azuredeploy.json", template); e != nil {
return e
}
if e := saveFileString(artifactsDir, "azuredeploy.parameters.json", parameters); e != nil {
return e
}
if certsGenerated {
if acsCluster.OrchestratorProfile.OrchestratorType == vlabs.Kubernetes {
directory := path.Join(artifactsDir, "kubeconfig")
for _, location := range tgen.AzureLocations {
b, gkcerr := tgen.GenerateKubeConfig(acsCluster, templateDirectory, location)
if gkcerr != nil {
return gkcerr
}
if e := saveFileString(directory, fmt.Sprintf("kubeconfig.%s.json", location), b); e != nil {
return e
}
}
}
if e := saveFileString(artifactsDir, "ca.key", acsCluster.CertificateProfile.GetCAPrivateKey()); e != nil {
return e
}
if e := saveFileString(artifactsDir, "ca.crt", acsCluster.CertificateProfile.CaCertificate); e != nil {
return e
}
if e := saveFileString(artifactsDir, "apiserver.key", acsCluster.CertificateProfile.APIServerPrivateKey); e != nil {
return e
}
if e := saveFileString(artifactsDir, "apiserver.crt", acsCluster.CertificateProfile.APIServerCertificate); e != nil {
return e
}
if e := saveFileString(artifactsDir, "client.key", acsCluster.CertificateProfile.ClientPrivateKey); e != nil {
return e
}
if e := saveFileString(artifactsDir, "client.crt", acsCluster.CertificateProfile.ClientCertificate); e != nil {
return e
}
if e := saveFileString(artifactsDir, "kubectlClient.key", acsCluster.CertificateProfile.KubeConfigPrivateKey); e != nil {
return e
}
if e := saveFileString(artifactsDir, "kubectlClient.crt", acsCluster.CertificateProfile.KubeConfigCertificate); e != nil {
return e
}
}
return nil
}
func saveFileString(dir string, file string, data string) error {
return saveFile(dir, file, []byte(data))
}
func saveFile(dir string, file string, data []byte) error {
if _, err := os.Stat(dir); os.IsNotExist(err) {
if e := os.MkdirAll(dir, 0700); e != nil {
return fmt.Errorf("error creating directory '%s': %s", dir, e.Error())
}
}
path := path.Join(dir, file)
if err := ioutil.WriteFile(path, []byte(data), 0600); err != nil {
return err
}
fmt.Fprintf(os.Stderr, "wrote %s\n", path)
return 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, "usage: %s [OPTIONS] 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")
@ -90,10 +182,13 @@ func usage(errs ...error) {
var templateDirectory = flag.String("templateDirectory", "./parts", "directory containing base template files")
var noPrettyPrint = flag.Bool("noPrettyPrint", false, "do not pretty print output")
var artifactsDir = flag.String("artifacts", "", "directory where artifacts will be written")
var classicMode = flag.Bool("classicMode", false, "enable classic parameters and outputs")
func main() {
var acsCluster *vlabs.AcsCluster
var template string
var parameters string
var err error
flag.Parse()
@ -114,7 +209,7 @@ func main() {
os.Exit(1)
}
if err = clustertemplate.VerifyFiles(*templateDirectory); err != nil {
if err = tgen.VerifyFiles(*templateDirectory); err != nil {
fmt.Fprintf(os.Stderr, "verification failed: %s\n", err.Error())
os.Exit(1)
}
@ -124,7 +219,17 @@ func main() {
os.Exit(1)
}
if template, err = clustertemplate.GenerateTemplate(acsCluster, *templateDirectory); err != nil {
certsGenerated := false
if certsGenerated, err = tgen.SetAcsClusterDefaults(acsCluster); err != nil {
fmt.Fprintf(os.Stderr, "error while setting defaults %s: %s", jsonFile, err.Error())
os.Exit(1)
}
if *classicMode {
acsCluster.SetClassicMode(true)
}
if template, parameters, err = tgen.GenerateTemplate(acsCluster, *templateDirectory); err != nil {
fmt.Fprintf(os.Stderr, "error generating template %s: %s", jsonFile, err.Error())
os.Exit(1)
}
@ -134,6 +239,14 @@ func main() {
fmt.Fprintf(os.Stderr, "error pretty printing template %s", err.Error())
os.Exit(1)
}
if parameters, err = prettyPrintArmTemplate(parameters); err != nil {
fmt.Fprintf(os.Stderr, "error pretty printing template %s", err.Error())
os.Exit(1)
}
}
if err = writeArtifacts(acsCluster, template, parameters, *artifactsDir, *templateDirectory, certsGenerated); err != nil {
fmt.Fprintf(os.Stderr, "error writing artifacts %s", err.Error())
os.Exit(1)
}
fmt.Print(template)
}

Просмотреть файл

@ -9,6 +9,8 @@ const (
DCOS173 = "DCOS173"
// Swarm is the string constant for the Swarm orchestrator type
Swarm = "Swarm"
// Kubernetes is the string constant for the Kubernetes orchestrator type
Kubernetes = "Kubernetes"
// MinAgentCount are the minimum number of agents
MinAgentCount = 1
// MaxAgentCount are the maximum number of agents
@ -17,14 +19,16 @@ const (
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
// StorageExternal equates to VMSS where attached disks are unsupported (Default)
StorageExternal = "External"
// StorageVolumes equates to AS where attached disks are supported
StorageVolumes = "Volumes"
// StorageHAVolumes are managed disks that provide fault domain coverage for volumes.
StorageHAVolumes = "HAVolumes"
// OSTypeWindows specifies the Windows OS
OSTypeWindows = "Windows"
// OSTypeLinux specifies the Linux OS
OSTypeLinux = "Linux"
)

Просмотреть файл

@ -1,41 +0,0 @@
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()
}

Просмотреть файл

@ -1,2 +1,2 @@
// Package vlabs stores an experimental api model for acs
package vlabs // import "./api/vlabs"
package vlabs

Просмотреть файл

@ -2,10 +2,15 @@ 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 OrchestratorProfile `json:"orchestratorProfile"`
MasterProfile MasterProfile `json:"masterProfile"`
AgentPoolProfiles []AgentPoolProfile `json:"agentPoolProfiles"`
WindowsProfile WindowsProfile `json:"windowsProfile"`
LinuxProfile LinuxProfile `json:"linuxProfile"`
ServicePrincipalProfile ServicePrincipalProfile `json:"servicePrincipalProfile"`
CertificateProfile CertificateProfile `json:"certificateProfile"`
// classic mode is used to output parameters and outputs
classicMode bool
}
// OrchestratorProfile represents the type of orchestrator
@ -13,6 +18,32 @@ type OrchestratorProfile struct {
OrchestratorType string `json:"orchestratorType"`
}
// ServicePrincipalProfile contains the client and secret used by the cluster for Azure Resource CRUD
type ServicePrincipalProfile struct {
ClientID string `json:"servicePrincipalClientID,omitempty"`
Secret string `json:"servicePrincipalClientSecret,omitempty"`
}
// CertificateProfile represents the definition of the master cluster
type CertificateProfile struct {
// CaCertificate is the certificate authority certificate.
CaCertificate string `json:"caCertificate,omitempty"`
// ApiServerCertificate is the rest api server certificate, and signed by the CA
APIServerCertificate string `json:"apiServerCertificate,omitempty"`
// ApiServerPrivateKey is the rest api server private key, and signed by the CA
APIServerPrivateKey string `json:"apiServerPrivateKey,omitempty"`
// ClientCertificate is the certificate used by the client kubelet services and signed by the CA
ClientCertificate string `json:"clientCertificate,omitempty"`
// ClientPrivateKey is the private key used by the client kubelet services and signed by the CA
ClientPrivateKey string `json:"clientPrivateKey,omitempty"`
// KubeConfigCertificate is the client certificate used for kubectl cli and signed by the CA
KubeConfigCertificate string `json:"kubeConfigCertificate,omitempty"`
// KubeConfigPrivateKey is the client private key used for kubectl cli and signed by the CA
KubeConfigPrivateKey string `json:"kubeConfigPrivateKey,omitempty"`
// caPrivateKey is an internal field only set if generation required
caPrivateKey string
}
// MasterProfile represents the definition of the master cluster
type MasterProfile struct {
Count int `json:"count"`
@ -30,8 +61,9 @@ type AgentPoolProfile struct {
Count int `json:"count"`
VMSize string `json:"vmSize"`
DNSPrefix string `json:"dnsPrefix,omitempty"`
OSType string `json:"osType,omitempty"`
Ports []int `json:"ports,omitempty"`
IsStateful bool `json:"isStateful,omitempty"`
StorageType string `json:"storageType,omitempty"`
DiskSizesGB []int `json:"diskSizesGB,omitempty"`
VnetSubnetID string `json:"vnetSubnetID,omitempty"`
// subnet is internal
@ -48,12 +80,47 @@ type LinuxProfile struct {
} `json:"ssh"`
}
// WindowsProfile represents the windows parameters passed to the cluster
type WindowsProfile struct {
AdminUsername string `json:"adminUsername"`
AdminPassword string `json:"adminPassword"`
}
// APIObject defines the required functionality of an api object
type APIObject interface {
SetDefaults()
Validate() error
}
// GetClassicMode gets the classic mode for deciding to output classic parameters
func (a *AcsCluster) GetClassicMode() bool {
return a.classicMode
}
// SetClassicMode toggles classic parameters and outputs
func (a *AcsCluster) SetClassicMode(isClassicMode bool) {
a.classicMode = isClassicMode
}
// HasWindows returns true if the cluster contains windows
func (a *AcsCluster) HasWindows() bool {
for _, agentPoolProfile := range a.AgentPoolProfiles {
if agentPoolProfile.OSType == OSTypeWindows {
return true
}
}
return false
}
// GetCAPrivateKey returns the ca private key
func (c *CertificateProfile) GetCAPrivateKey() string {
return c.caPrivateKey
}
// SetCAPrivateKey sets the ca private key
func (c *CertificateProfile) SetCAPrivateKey(caPrivateKey string) {
c.caPrivateKey = caPrivateKey
}
// IsCustomVNET returns true if the customer brought their own VNET
func (m *MasterProfile) IsCustomVNET() bool {
return len(m.VnetSubnetID) > 0
@ -64,11 +131,26 @@ func (m *MasterProfile) GetSubnet() string {
return m.subnet
}
// SetSubnet sets the read-only subnet for the master
func (m *MasterProfile) SetSubnet(subnet string) {
m.subnet = subnet
}
// IsCustomVNET returns true if the customer brought their own VNET
func (a *AgentPoolProfile) IsCustomVNET() bool {
return len(a.VnetSubnetID) > 0
}
// IsWindows returns true if the agent pool is windows
func (a *AgentPoolProfile) IsWindows() bool {
return a.OSType == OSTypeWindows
}
// IsVolumeBasedStorage returns true if the customer specified disks
func (a *AgentPoolProfile) IsVolumeBasedStorage() bool {
return a.StorageType == StorageVolumes
}
// HasDisks returns true if the customer specified disks
func (a *AgentPoolProfile) HasDisks() bool {
return len(a.DiskSizesGB) > 0
@ -78,3 +160,8 @@ func (a *AgentPoolProfile) HasDisks() bool {
func (a *AgentPoolProfile) GetSubnet() string {
return a.subnet
}
// SetSubnet sets the read-only subnet for the agent pool
func (a *AgentPoolProfile) SetSubnet(subnet string) {
a.subnet = subnet
}

Просмотреть файл

@ -3,8 +3,8 @@ package vlabs
import (
"errors"
"fmt"
"net"
"regexp"
"strconv"
)
// Validate implements APIObject
@ -14,9 +14,11 @@ func (o *OrchestratorProfile) Validate() error {
case DCOS184:
case DCOS173:
case Swarm:
case Kubernetes:
default:
return fmt.Errorf("OrchestratorProfile has unknown orchestrator: %s", o.OrchestratorType)
}
return nil
}
@ -67,8 +69,8 @@ func (a *AgentPoolProfile) Validate() error {
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) > 0 && (a.StorageType != StorageVolumes && a.StorageType != StorageHAVolumes) {
return fmt.Errorf("Storage Type %s does not support attached disks for cluster named '%s'. Specify storage as either %s or %s", a.StorageType, a.Name, StorageVolumes, StorageHAVolumes)
}
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)
@ -104,12 +106,49 @@ func (a *AcsCluster) Validate() error {
if e := validateUniqueProfileNames(a.AgentPoolProfiles); e != nil {
return e
}
if a.OrchestratorProfile.OrchestratorType == Kubernetes && len(a.ServicePrincipalProfile.ClientID) == 0 {
return fmt.Errorf("the service principal client ID must be specified with Orchestrator %s", a.OrchestratorProfile.OrchestratorType)
}
if a.OrchestratorProfile.OrchestratorType == Kubernetes && len(a.ServicePrincipalProfile.Secret) == 0 {
return fmt.Errorf("the service principal client secrect must be specified with Orchestrator %s", a.OrchestratorProfile.OrchestratorType)
}
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")
switch agentPoolProfile.StorageType {
case StorageVolumes:
case StorageHAVolumes:
case StorageExternal:
case "":
default:
{
return fmt.Errorf("unknown storage type '%s' for agent pool '%s'. Specify one of %s, %s, or %s", agentPoolProfile.StorageType, agentPoolProfile.Name, StorageExternal, StorageVolumes, StorageHAVolumes)
}
}
if agentPoolProfile.StorageType == StorageHAVolumes {
return errors.New("HA volumes are currently unsupported")
}
if a.OrchestratorProfile.OrchestratorType == Kubernetes && (agentPoolProfile.StorageType == StorageExternal || len(agentPoolProfile.StorageType) == 0) {
return fmt.Errorf("External storage deployments (VMSS) are not supported with Kubernetes since Kubernetes requires the ability to attach/detach disks. To fix specify \"StorageType\":\"%s\"", StorageVolumes)
}
if a.OrchestratorProfile.OrchestratorType == Kubernetes && len(agentPoolProfile.DNSPrefix) > 0 {
return errors.New("DNSPrefix not support for agent pools in Kubernetes - Kubernetes marks its own clusters public")
}
if agentPoolProfile.OSType == OSTypeWindows {
switch a.OrchestratorProfile.OrchestratorType {
case Swarm:
default:
return fmt.Errorf("Orchestrator %s does not support Windows", a.OrchestratorProfile.OrchestratorType)
}
if len(a.WindowsProfile.AdminUsername) == 0 {
return fmt.Errorf("WindowsProfile.AdminUsername must not be empty since agent pool '%s' specifies windows", agentPoolProfile.Name)
}
if len(a.WindowsProfile.AdminPassword) == 0 {
return fmt.Errorf("WindowsProfile.AdminPassword must not be empty since agent pool '%s' specifies windows", agentPoolProfile.Name)
}
}
}
if e := a.LinuxProfile.Validate(); e != nil {
@ -128,36 +167,6 @@ func validateName(name string, label string) error {
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})$`
@ -185,33 +194,6 @@ func validateDNSName(dnsName string) error {
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 {
@ -242,9 +224,6 @@ func validateVNET(a *AcsCluster) error {
}
}
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
@ -262,9 +241,9 @@ func validateVNET(a *AcsCluster) error {
}
}
// 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
masterFirstIP := net.ParseIP(a.MasterProfile.FirstConsecutiveStaticIP)
if masterFirstIP == nil {
return fmt.Errorf("MasterProfile.FirstConsecutiveStaticIP (with VNET Subnet specification) '%s' is an invalid IP address", a.MasterProfile.FirstConsecutiveStaticIP)
}
}
return nil

Просмотреть файл

@ -4,7 +4,7 @@
},
"masterProfile": {
"count": 3,
"dnsPrefix": "mgmtanhowe0927i",
"dnsPrefix": "mgmtanhowe1009i",
"vmSize": "Standard_D2_v2"
},
"agentPoolProfiles": [
@ -17,7 +17,7 @@
"name": "agentpublic",
"count": 3,
"vmSize": "Standard_D2_v2",
"dnsPrefix": "appanhowe0927i",
"dnsPrefix": "appanhowe1009i",
"ports": [
80,
443,

Просмотреть файл

@ -4,7 +4,7 @@
},
"masterProfile": {
"count": 1,
"dnsPrefix": "mgmtanhowe0926i",
"dnsPrefix": "mgmtanhowe1009g",
"vmSize": "Standard_D2_v2"
},
"agentPoolProfiles": [
@ -17,7 +17,7 @@
"name": "agentpool2",
"count": 3,
"vmSize": "Standard_D2_v2",
"dnsPrefix": "appanhowe0926i",
"dnsPrefix": "appanhowe1009g",
"ports": [
80,
443,

Просмотреть файл

@ -1,11 +1,11 @@
{
"orchestratorProfile": {
"orchestratorType": "DCOS"
"orchestratorType": "DCOS184"
},
"masterProfile": {
"count": 5,
"dnsPrefix": "mgmtanhowe0926i",
"vmSize": "Standard_D2_v2"
"count": 3,
"dnsPrefix": "mgmtanhowe1009h",
"vmSize": "Standard_D2_v2"
},
"agentPoolProfiles": [
{
@ -17,7 +17,7 @@
"name": "agentpublic",
"count": 3,
"vmSize": "Standard_D2_v2",
"dnsPrefix": "appanhowe0926i",
"dnsPrefix": "appanhowe1009h",
"ports": [
80,
443,

Просмотреть файл

@ -1,70 +0,0 @@
{
"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"
}
]
}
}
}

Просмотреть файл

@ -0,0 +1,38 @@
{
"orchestratorProfile": {
"orchestratorType": "Kubernetes"
},
"masterProfile": {
"count": 1,
"dnsPrefix": "mgmtanhowe1004f",
"vmSize": "Standard_D2_v2"
},
"agentPoolProfiles": [
{
"name": "agentpool1",
"count": 3,
"vmSize": "Standard_D2_v2",
"StorageType": "Volumes"
},
{
"name": "agentpool2",
"count": 3,
"vmSize": "Standard_D2_v2",
"StorageType": "Volumes"
}
],
"linuxProfile": {
"adminUsername": "azureuser",
"ssh": {
"publicKeys": [
{
"keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8fhkh3jpHUQsrUIezFB5k4Rq9giJM8G1Cr0u2IRMiqG++nat5hbOr3gODpTA0h11q9bzb6nJtK7NtDzIHx+w3YNIVpcTGLiUEsfUbY53IHg7Nl/p3/gkST3g0R6BSL7Hg45SfyvpH7kwY30MoVHG/6P3go4SKlYoHXlgaaNr3fMwUTIeE9ofvyS3fcr6xxlsoB6luKuEs50h0NGsE4QEnbfSY4Yd/C1ucc3mEw+QFXBIsENHfHfZYrLNHm2L8MXYVmAH8k//5sFs4Migln9GiUgEQUT6uOjowsZyXBbXwfT11og+syPkAq4eqjiC76r0w6faVihdBYVoc/UcyupgH azureuser@linuxvm"
}
]
}
},
"servicePrincipalProfile": {
"servicePrincipalClientID": "[REPLACE-WITH-ServicePrincipalClientID",
"servicePrincipalClientSecret": "[REPLACE-WITH-myServicePrincipalClientSecret"
}
}

Просмотреть файл

@ -4,7 +4,7 @@
},
"masterProfile": {
"count": 1,
"dnsPrefix": "mgmtanhowe0927c",
"dnsPrefix": "mgmtanhowe1009r",
"vmSize": "Standard_D2_v2"
},
"agentPoolProfiles": [
@ -12,7 +12,7 @@
"name": "agentpool1",
"count": 3,
"vmSize": "Standard_D2_v2",
"dnsPrefix": "appanhowe0927c",
"dnsPrefix": "appanhowe1009r",
"ports": [
80,
443,

Просмотреть файл

@ -0,0 +1,29 @@
{
"$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": "KubernetesCustomVNET",
"properties": {
"addressSpace": {
"addressPrefixes": [
"10.239.0.0/16"
]
},
"subnets": [
{
"name": "KubernetesSubnet",
"properties": {
"addressPrefix": "10.239.0.0/16"
}
}
]
},
"type": "Microsoft.Network/virtualNetworks"
}
]
}

Просмотреть файл

@ -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"
}
]
}

Просмотреть файл

@ -3,24 +3,25 @@
"orchestratorType": "DCOS"
},
"masterProfile": {
"count": 5,
"dnsPrefix": "mgmtanhowe0927h",
"count": 3,
"dnsPrefix": "mgmtanhowe1009f",
"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",
"name": "agentprivate",
"count": 3,
"vmSize": "Standard_D2_v2",
"StorageType": "Volumes",
"vnetSubnetId": "/subscriptions/b52fce95-de5f-4b37-afca-db203a5d0b6a/resourceGroups/anhoweExampleRG/providers/Microsoft.Network/virtualNetworks/ExampleCustomVNET/subnets/ExampleAgentSubnet"
},
{
"name": "agentPublic",
"name": "agentpublic",
"count": 3,
"vmSize": "Standard_D2_v2",
"dnsPrefix": "appanhowe0927h",
"dnsPrefix": "appanhowe1009f",
"vnetSubnetId": "/subscriptions/b52fce95-de5f-4b37-afca-db203a5d0b6a/resourceGroups/anhoweExampleRG/providers/Microsoft.Network/virtualNetworks/ExampleCustomVNET/subnets/ExampleAgentSubnet",
"ports": [
80,

Просмотреть файл

@ -1,9 +1,11 @@
$VerbosePreference="Continue"
$deployName="anhoweExampleRG"
$deployName="anhoweKubeVnet"
$RGName=$deployName
$SubscriptionId="b52fce95-de5f-4b37-afca-db203a5d0b6a"
Set-AzureRmContext -SubscriptionId $SubscriptionId
$locName="West US"
$templateFile = "azuredeploy.json"
#$templateFile = "azuredeploy.dcos.json"
#$templateFile = "azuredeploy.swarm.json"
$templateFile = "azuredeploy.kubernetes.json"
New-AzureRmResourceGroup -Name $RGName -Location $locName -Force
New-AzureRmResourceGroupDeployment -Name $deployName -ResourceGroupName $RGName -TemplateFile $templateFile

Просмотреть файл

@ -0,0 +1,42 @@
{
"orchestratorProfile": {
"orchestratorType": "Kubernetes"
},
"masterProfile": {
"count": 1,
"dnsPrefix": "anhowe1009d",
"vmSize": "Standard_D2_v2",
"vnetSubnetId": "/subscriptions/b52fce95-de5f-4b37-afca-db203a5d0b6a/resourceGroups/anhoweKubeVnet/providers/Microsoft.Network/virtualNetworks/KubernetesCustomVNET/subnets/KubernetesSubnet",
"firstConsecutiveStaticIP": "10.239.255.245"
},
"agentPoolProfiles": [
{
"name": "agentpri",
"count": 2,
"vmSize": "Standard_D2_v2",
"vnetSubnetId": "/subscriptions/b52fce95-de5f-4b37-afca-db203a5d0b6a/resourceGroups/anhoweKubeVnet/providers/Microsoft.Network/virtualNetworks/KubernetesCustomVNET/subnets/KubernetesSubnet",
"StorageType": "Volumes"
},
{
"name": "agentpri2",
"count": 2,
"vmSize": "Standard_D2_v2",
"vnetSubnetId": "/subscriptions/b52fce95-de5f-4b37-afca-db203a5d0b6a/resourceGroups/anhoweKubeVnet/providers/Microsoft.Network/virtualNetworks/KubernetesCustomVNET/subnets/KubernetesSubnet",
"StorageType": "Volumes"
}
],
"linuxProfile": {
"adminUsername": "azureuser",
"ssh": {
"publicKeys": [
{
"keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8fhkh3jpHUQsrUIezFB5k4Rq9giJM8G1Cr0u2IRMiqG++nat5hbOr3gODpTA0h11q9bzb6nJtK7NtDzIHx+w3YNIVpcTGLiUEsfUbY53IHg7Nl/p3/gkST3g0R6BSL7Hg45SfyvpH7kwY30MoVHG/6P3go4SKlYoHXlgaaNr3fMwUTIeE9ofvyS3fcr6xxlsoB6luKuEs50h0NGsE4QEnbfSY4Yd/C1ucc3mEw+QFXBIsENHfHfZYrLNHm2L8MXYVmAH8k//5sFs4Migln9GiUgEQUT6uOjowsZyXBbXwfT11og+syPkAq4eqjiC76r0w6faVihdBYVoc/UcyupgH azureuser@linuxvm"
}
]
}
},
"servicePrincipalProfile": {
"servicePrincipalClientID": "[REPLACE-WITH-ServicePrincipalClientID",
"servicePrincipalClientSecret": "[REPLACE-WITH-myServicePrincipalClientSecret"
}
}

Просмотреть файл

@ -0,0 +1,42 @@
{
"orchestratorProfile": {
"orchestratorType": "Swarm"
},
"masterProfile": {
"count": 3,
"dnsPrefix": "mgmtanhowe1009j",
"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": "appanhowe1009j",
"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"
}
]
}
}
}

Просмотреть файл

@ -0,0 +1,42 @@
{
"orchestratorProfile": {
"orchestratorType": "DCOS"
},
"masterProfile": {
"count": 3,
"dnsPrefix": "mgmtanhowe1009o",
"vmSize": "Standard_D2_v2"
},
"agentPoolProfiles": [
{
"name": "agent128",
"count": 3,
"vmSize": "Standard_D2_v2",
"StorageType": "Volumes",
"diskSizesGB": [128, 128, 128, 128]
},
{
"name": "agent1public",
"count": 3,
"vmSize": "Standard_D2_v2",
"dnsPrefix": "appanhowe1009o",
"StorageType": "Volumes",
"diskSizesGB": [1],
"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"
}
]
}
}
}

Просмотреть файл

@ -4,21 +4,27 @@
},
"masterProfile": {
"count": 3,
"dnsPrefix": "mgmtanhowe0928d",
"dnsPrefix": "mgmtanhowe1009n",
"vmSize": "Standard_D2_v2"
},
"agentPoolProfiles": [
{
"name": "agtnodisks1",
"count": 1,
"name": "agent128",
"count": 3,
"vmSize": "Standard_D2_v2",
"isStateful": true
"StorageType": "Volumes"
},
{
"name": "agtnodisks2",
"count": 1,
"name": "agent1public",
"count": 3,
"vmSize": "Standard_D2_v2",
"isStateful": true
"dnsPrefix": "appanhowe1009n",
"StorageType": "Volumes",
"ports": [
80,
443,
8080
]
}
],
"linuxProfile": {

Просмотреть файл

@ -0,0 +1,40 @@
{
"orchestratorProfile": {
"orchestratorType": "Kubernetes"
},
"masterProfile": {
"count": 1,
"dnsPrefix": "mgmtanhowe1004f",
"vmSize": "Standard_D2_v2"
},
"agentPoolProfiles": [
{
"name": "agentpool1",
"count": 3,
"vmSize": "Standard_D2_v2",
"StorageType": "Volumes",
"diskSizesGB": [128, 128, 128, 128]
},
{
"name": "agentpool2",
"count": 3,
"vmSize": "Standard_D2_v2",
"StorageType": "Volumes",
"diskSizesGB": [10, 10, 10, 10]
}
],
"linuxProfile": {
"adminUsername": "azureuser",
"ssh": {
"publicKeys": [
{
"keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8fhkh3jpHUQsrUIezFB5k4Rq9giJM8G1Cr0u2IRMiqG++nat5hbOr3gODpTA0h11q9bzb6nJtK7NtDzIHx+w3YNIVpcTGLiUEsfUbY53IHg7Nl/p3/gkST3g0R6BSL7Hg45SfyvpH7kwY30MoVHG/6P3go4SKlYoHXlgaaNr3fMwUTIeE9ofvyS3fcr6xxlsoB6luKuEs50h0NGsE4QEnbfSY4Yd/C1ucc3mEw+QFXBIsENHfHfZYrLNHm2L8MXYVmAH8k//5sFs4Migln9GiUgEQUT6uOjowsZyXBbXwfT11og+syPkAq4eqjiC76r0w6faVihdBYVoc/UcyupgH azureuser@linuxvm"
}
]
}
},
"servicePrincipalProfile": {
"servicePrincipalClientID": "[REPLACE-WITH-ServicePrincipalClientID",
"servicePrincipalClientSecret": "[REPLACE-WITH-myServicePrincipalClientSecret"
}
}

Просмотреть файл

@ -0,0 +1,42 @@
{
"orchestratorProfile": {
"orchestratorType": "Swarm"
},
"masterProfile": {
"count": 3,
"dnsPrefix": "mgmtanhowe1009q",
"vmSize": "Standard_D2_v2"
},
"agentPoolProfiles": [
{
"name": "agent128",
"count": 3,
"vmSize": "Standard_D2_v2",
"StorageType": "Volumes",
"diskSizesGB": [128, 128, 128, 128]
},
{
"name": "agent1public",
"count": 3,
"vmSize": "Standard_D2_v2",
"dnsPrefix": "appanhowe1009q",
"StorageType": "Volumes",
"diskSizesGB": [1],
"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"
}
]
}
}
}

Просмотреть файл

@ -0,0 +1,40 @@
{
"orchestratorProfile": {
"orchestratorType": "Swarm"
},
"masterProfile": {
"count": 3,
"dnsPrefix": "mgmtanhowe1009p",
"vmSize": "Standard_D2_v2"
},
"agentPoolProfiles": [
{
"name": "agent128",
"count": 3,
"vmSize": "Standard_D2_v2",
"StorageType": "Volumes"
},
{
"name": "agent1public",
"count": 3,
"vmSize": "Standard_D2_v2",
"dnsPrefix": "appanhowe1009p",
"StorageType": "Volumes",
"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"
}
]
}
}
}

Просмотреть файл

@ -0,0 +1,44 @@
{
"orchestratorProfile": {
"orchestratorType": "Swarm"
},
"masterProfile": {
"count": 1,
"dnsPrefix": "mgmtanhowe1009d",
"vmSize": "Standard_D2_v2"
},
"agentPoolProfiles": [
{
"name": "publicwin1",
"count": 3,
"vmSize": "Standard_D2_v2",
"dnsPrefix": "appanhowe1009d",
"osType": "Windows",
"ports": [
80,
443,
8080
]
},
{
"name": "privatewin",
"count": 3,
"vmSize": "Standard_D2_v2",
"osType": "Windows"
}
],
"windowsProfile": {
"adminUsername": "azureuser",
"adminPassword": "replacepassword1234$"
},
"linuxProfile": {
"adminUsername": "azureuser",
"ssh": {
"publicKeys": [
{
"keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8fhkh3jpHUQsrUIezFB5k4Rq9giJM8G1Cr0u2IRMiqG++nat5hbOr3gODpTA0h11q9bzb6nJtK7NtDzIHx+w3YNIVpcTGLiUEsfUbY53IHg7Nl/p3/gkST3g0R6BSL7Hg45SfyvpH7kwY30MoVHG/6P3go4SKlYoHXlgaaNr3fMwUTIeE9ofvyS3fcr6xxlsoB6luKuEs50h0NGsE4QEnbfSY4Yd/C1ucc3mEw+QFXBIsENHfHfZYrLNHm2L8MXYVmAH8k//5sFs4Migln9GiUgEQUT6uOjowsZyXBbXwfT11og+syPkAq4eqjiC76r0w6faVihdBYVoc/UcyupgH azureuser@linuxvm"
}
]
}
}
}

Просмотреть файл

@ -0,0 +1,50 @@
{
"orchestratorProfile": {
"orchestratorType": "Swarm"
},
"masterProfile": {
"count": 1,
"dnsPrefix": "mgmtanhowe1009f",
"vmSize": "Standard_D2_v2"
},
"agentPoolProfiles": [
{
"name": "publicwin1",
"count": 3,
"vmSize": "Standard_D2_v2",
"dnsPrefix": "appanhowe1009f",
"osType": "Windows",
"ports": [
80,
443,
8080
]
},
{
"name": "privatewin",
"count": 3,
"vmSize": "Standard_D2_v2",
"osType": "Windows"
},
{
"name": "privatelinux",
"count": 3,
"vmSize": "Standard_D2_v2",
"osType": "Linux"
}
],
"windowsProfile": {
"adminUsername": "azureuser",
"adminPassword": "replacepassword1234$"
},
"linuxProfile": {
"adminUsername": "azureuser",
"ssh": {
"publicKeys": [
{
"keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8fhkh3jpHUQsrUIezFB5k4Rq9giJM8G1Cr0u2IRMiqG++nat5hbOr3gODpTA0h11q9bzb6nJtK7NtDzIHx+w3YNIVpcTGLiUEsfUbY53IHg7Nl/p3/gkST3g0R6BSL7Hg45SfyvpH7kwY30MoVHG/6P3go4SKlYoHXlgaaNr3fMwUTIeE9ofvyS3fcr6xxlsoB6luKuEs50h0NGsE4QEnbfSY4Yd/C1ucc3mEw+QFXBIsENHfHfZYrLNHm2L8MXYVmAH8k//5sFs4Migln9GiUgEQUT6uOjowsZyXBbXwfT11og+syPkAq4eqjiC76r0w6faVihdBYVoc/UcyupgH azureuser@linuxvm"
}
]
}
}
}

Просмотреть файл

@ -0,0 +1,46 @@
{
"orchestratorProfile": {
"orchestratorType": "Swarm"
},
"masterProfile": {
"count": 1,
"dnsPrefix": "mgmtanhowe1009e",
"vmSize": "Standard_D2_v2"
},
"agentPoolProfiles": [
{
"name": "publicwin1",
"count": 3,
"vmSize": "Standard_D2_v2",
"dnsPrefix": "appanhowe1009e",
"osType": "Windows",
"StorageType": "Volumes",
"ports": [
80,
443,
8080
]
},
{
"name": "privatewin",
"count": 3,
"vmSize": "Standard_D2_v2",
"StorageType": "Volumes",
"osType": "Windows"
}
],
"windowsProfile": {
"adminUsername": "azureuser",
"adminPassword": "replacepassword1234$"
},
"linuxProfile": {
"adminUsername": "azureuser",
"ssh": {
"publicKeys": [
{
"keyData": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC8fhkh3jpHUQsrUIezFB5k4Rq9giJM8G1Cr0u2IRMiqG++nat5hbOr3gODpTA0h11q9bzb6nJtK7NtDzIHx+w3YNIVpcTGLiUEsfUbY53IHg7Nl/p3/gkST3g0R6BSL7Hg45SfyvpH7kwY30MoVHG/6P3go4SKlYoHXlgaaNr3fMwUTIeE9ofvyS3fcr6xxlsoB6luKuEs50h0NGsE4QEnbfSY4Yd/C1ucc3mEw+QFXBIsENHfHfZYrLNHm2L8MXYVmAH8k//5sFs4Migln9GiUgEQUT6uOjowsZyXBbXwfT11og+syPkAq4eqjiC76r0w6faVihdBYVoc/UcyupgH azureuser@linuxvm"
}
]
}
}
}

Просмотреть файл

@ -1,2 +0,0 @@
// Package clustertemplate stores an experimental api model for acs
package clustertemplate // import "./clustertemplate"

Просмотреть файл

@ -1,392 +0,0 @@
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
}

Просмотреть файл

@ -1,163 +1,12 @@
"{{.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}},
"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],
"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}}",
{{GetAgentAllowedSizes}}
"metadata": {
"description": "The size of the Virtual Machine."
},
@ -165,7 +14,6 @@
},
{{if .IsCustomVNET}}
"{{.Name}}VnetSubnetID": {
"defaultValue": "{{.VnetSubnetID}}",
"metadata": {
"description": "Sets the vnet subnet of agent pool '{{.Name}}'."
},
@ -182,7 +30,6 @@
{{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."
},

Просмотреть файл

@ -0,0 +1,166 @@
"agentEndpointDNSNamePrefix": {
"defaultValue": "UNUSED",
"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"
},
"disablePasswordAuthentication": {
"defaultValue": true,
"metadata": {
"description": "This setting controls whether password auth is disabled for Linux VMs provisioned by this template. Default is true which disables password and makes SSH key required."
},
"type": "bool"
},
"enableNewStorageAccountNaming": {
"defaultValue": true,
"metadata": {
"description": "If true: uses DNS name prefix + Orchestrator name + Region to create storage account name to reduce name collision probability. If false: uses DNS name prefix + Orchestrator name to create storage account name to maintain template idempotency."
},
"type": "bool"
},
"enableVMDiagnostics": {
"defaultValue": true,
"metadata": {
"description": "Allows user to enable/disable boot & vm diagnostics."
},
"type": "bool"
},
"isValidation": {
"allowedValues": [
0,
1
],
"defaultValue": 0,
"metadata": {
"description": "This is testing in the validation region"
},
"type": "int"
},
"jumpboxEndpointDNSNamePrefix": {
"defaultValue": "UNUSED",
"metadata": {
"description": "Sets the Domain name label for the jumpbox. The concatenation of the domain name label and the regionalized DNS zone make up the fully qualified domain name associated with the public IP address."
},
"type": "string"
},
"linuxAdminPassword": {
"defaultValue": "UNUSED",
"metadata": {
"description": "Password for the Linux Virtual Machine. Not Required. If not set, you must provide a SSH key."
},
"type": "securestring"
},
"linuxOffer": {
"defaultValue": "UNUSED",
"metadata": {
"description": "This is the offer of the image used by the linux cluster"
},
"type": "string"
},
"linuxPublisher": {
"defaultValue": "UNUSED",
"metadata": {
"description": "This is the publisher of the image used by the linux cluster"
},
"type": "string"
},
"linuxSku": {
"defaultValue": "UNUSED",
"metadata": {
"description": "This is the linux sku used by the linux cluster"
},
"type": "string"
},
"linuxVersion": {
"defaultValue": "UNUSED",
"metadata": {
"description": "This is the linux version used by the linux cluster"
},
"type": "string"
},
"masterCount": {
"allowedValues": [
1,
3,
5
],
"defaultValue": 1,
"metadata": {
"description": "The number of Mesos masters for the cluster."
},
"type": "int"
},
"oauthEnabled": {
"allowedValues": [
"true",
"false"
],
"defaultValue": "false",
"metadata": {
"description": "Enable OAuth authentication"
},
"type": "string"
},
"postInstallScriptURI": {
"defaultValue": "disabled",
"metadata": {
"description": "After installation, this specifies a script to download and install. To disabled, set value to 'disabled'."
},
"type": "string"
},
"setLinuxConfigurationForVMCreate": {
"allowedValues": [
0,
1
],
"defaultValue": 1,
"metadata": {
"description": "This setting controls whether Linux configuration with SSH Key is passed in VM PUT Payload. Defaults to 1. If SSH Key is blank, this must be set to 0."
},
"type": "int"
},
"vmsPerStorageAccount": {
"defaultValue": 5,
"metadata": {
"description": "This specifies the number of VMs per storage accounts"
},
"type": "int"
},
{{if not .HasWindows}}
"windowsAdminPassword": {
"defaultValue": "UNUSED",
"metadata": {
"description": "Password for the Windows Virtual Machine."
},
"type": "securestring"
},
"windowsAdminUsername": {
"defaultValue": "UNUSED",
"metadata": {
"description": "User name for the Windows Virtual Machine (Password Only Supported)."
},
"type": "string"
},
{{end}}
"windowsJumpboxOffer": {
"defaultValue": "UNUSED",
"metadata": {
"description": "This is the windows offer used by the windows"
},
"type": "string"
},
"windowsJumpboxPublisher": {
"defaultValue": "UNUSED",
"metadata": {
"description": "This is the windows publisher used by the windows"
},
"type": "string"
},
"windowsJumpboxSku": {
"defaultValue": "UNUSED",
"metadata": {
"description": "This is the windows sku used by the windows"
},
"type": "string"
}

Просмотреть файл

@ -18,7 +18,7 @@
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]"
],
"location": "[variables('storageLocation')]",
"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]"

Просмотреть файл

@ -63,7 +63,7 @@
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]"
],
"location": "[variables('storageLocation')]",
"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]"
@ -80,7 +80,7 @@
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]"
],
"location": "[variables('storageLocation')]",
"location": "[resourceGroup().location]",
"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]"

Просмотреть файл

@ -1,6 +1,6 @@
"{{.Name}}StorageAccountOffset": "[mul(variables('maxStorageAccountsPerAgent'),variables('{{.Name}}Index'))]",
"{{.Name}}Count": "[parameters('{{.Name}}Count')]",
{{if .IsStateful}}
{{if .IsVolumeBasedStorage}}
"{{.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}}

Просмотреть файл

@ -8,18 +8,20 @@
"variables": {
{{range $index, $agent := .AgentPoolProfiles}}
{{template "dcosagentvars.t" .}}
{{if .IsStateful}}
{{if .IsVolumeBasedStorage}}
"{{.Name}}DataAccountName": "[concat(variables('storageAccountBaseName'), 'data{{$index}}')]",
{{end}}
"{{.Name}}Index": {{$index}},
"{{.Name}}AccountName": "[concat(variables('storageAccountBaseName'), 'agnt{{$index}}')]",
{{end}}
{{template "dcosmastervars.t" .}}
{{template "dcosmastervars.t" .}},
{{GetSizeMap}}
},
"resources": [
{{range .AgentPoolProfiles}}
{{if .IsStateful}}
{{if .IsVolumeBasedStorage}}
{{template "dcosagentresourcesdisks.t" .}},
{{else}}
{{template "dcosagentresources.t" .}},

Просмотреть файл

@ -3,7 +3,7 @@
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]"
],
"location": "[variables('storageLocation')]",
"location": "[resourceGroup().location]",
"name": "[variables('masterStorageAccountName')]",
"properties": {
"accountType": "[variables('vmSizesMap')[variables('masterVMSize')].storageAccountType]"
@ -15,7 +15,7 @@
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]"
],
"location": "[variables('storageLocation')]",
"location": "[resourceGroup().location]",
"name": "[variables('masterStorageAccountExhibitorName')]",
"properties": {
"accountType": "Standard_LRS"
@ -141,12 +141,13 @@
"name": "nicLoopNode"
},
"dependsOn": [
{{if not .MasterProfile.IsCustomVNET}}
{{if .MasterProfile.IsCustomVNET}}
"[variables('masterNSGID')]",
{{else}}
"[variables('vnetID')]",
{{end}}
"[variables('masterLbID')]",
"[concat(variables('masterLbID'),'/inboundNatRules/SSH-',variables('masterVMNamePrefix'),copyIndex())]",
"[variables('masterNSGID')]"
"[concat(variables('masterLbID'),'/inboundNatRules/SSH-',variables('masterVMNamePrefix'),copyIndex())]"
],
"location": "[resourceGroup().location]",
"name": "[concat(variables('masterVMNamePrefix'), 'nic-', copyIndex())]",
@ -172,10 +173,12 @@
}
}
}
],
"networkSecurityGroup": {
]
{{if .MasterProfile.IsCustomVNET}}
,"networkSecurityGroup": {
"id": "[variables('masterNSGID')]"
}
{{end}}
},
"type": "Microsoft.Network/networkInterfaces"
},

Просмотреть файл

@ -41,197 +41,16 @@
"[concat(variables('masterVMNamePrefix'), 'nic-6')]"
],
"masterVMSize": "[parameters('masterVMSize')]",
"nameSuffix": "{{GetUniqueNameSuffix}}",
"nameSuffix": "[parameters('nameSuffix')]",
"oauthEnabled": "false",
"orchestratorName": "dcos",
"osImageOffer": "UbuntuServer",
"osImagePublisher": "Canonical",
"osImageSKU": "16.04.0-LTS",
"osImageVersion": "16.04.201606270",
"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"
],
"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"
}
}
"storageAccountType": "Standard_LRS"

Просмотреть файл

@ -0,0 +1,32 @@
{
"apiVersion": "v1",
"clusters": [
{
"cluster": {
"certificate-authority-data": "<<<variables('caCertificate')>>>",
"server": "https://<<<reference(concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))).dnsSettings.fqdn>>>"
},
"name": "{{{resourceGroup}}}"
}
],
"contexts": [
{
"context": {
"cluster": "{{{resourceGroup}}}",
"user": "{{{resourceGroup}}}-admin"
},
"name": "{{{resourceGroup}}}"
}
],
"current-context": "{{{resourceGroup}}}",
"kind": "Config",
"users": [
{
"name": "{{{resourceGroup}}}-admin",
"user": {
"client-certificate-data": "<<<variables('kubeConfigCertificate')>>>",
"client-key-data": "<<<variables('kubeConfigPrivateKey')>>>"
}
}
]
}

Просмотреть файл

@ -0,0 +1,152 @@
#cloud-config
# { { { variable } } }
runcmd:
- /usr/bin/curl -sSL --retry 12 --retry-delay 10 https://get.docker.com/ > /tmp/install-docker
- /bin/bash -c "/bin/bash /tmp/install-docker"
write_files:
- path: "/etc/systemd/system/docker.service.d/clear_mount_propagation_flags.conf"
permissions: "0644"
owner: "root"
content: |
[Service]
MountFlags=shared
- path: "/etc/systemd/system/docker.service.d/overlay.conf"
permissions: "0644"
owner: "root"
content: |
[Service]
ExecStart=
ExecStart=/usr/bin/docker daemon -H fd:// --storage-driver=overlay
- path: "/etc/kubernetes/certs/ca.crt"
permissions: "0644"
encoding: "base64"
owner: "root"
content: |
{{{caCertificate}}}
- path: "/etc/kubernetes/certs/apiserver.crt"
permissions: "0644"
encoding: "base64"
owner: "root"
content: |
{{{apiserverCertificate}}}
- path: "/etc/kubernetes/certs/client.crt"
permissions: "0644"
encoding: "base64"
owner: "root"
content: |
{{{clientCertificate}}}
- path: "/var/lib/kubelet/kubeconfig"
permissions: "0644"
owner: "root"
content: |
apiVersion: v1
kind: Config
clusters:
- name: localcluster
cluster:
certificate-authority: /etc/kubernetes/certs/ca.crt
server: https://{{{masterPrivateIp}}}:443
users:
- name: client
user:
client-certificate: /etc/kubernetes/certs/client.crt
client-key: /etc/kubernetes/certs/client.key
contexts:
- context:
cluster: localcluster
user: client
name: localclustercontext
current-context: localclustercontext
- path: /etc/kubernetes/manifests/node.yaml
permissions: "0644"
owner: "root"
content: |
apiVersion: "v1"
kind: "Pod"
metadata:
name: "k8s-node"
namespace: "kube-system"
spec:
hostNetwork: true
containers:
- name: "kube-proxy"
image: "{{{kubernetesHyperkubeSpec}}}"
command:
- "/hyperkube"
- "proxy"
- "--kubeconfig=/var/lib/kubelet/kubeconfig"
- "--proxy-mode=iptables"
- "--v=2"
securityContext:
privileged: true
volumeMounts:
- name: "etc-kubernetes"
mountPath: "/etc/kubernetes"
- name: "var-lib-kubelet"
mountPath: "/var/lib/kubelet"
volumes:
- name: "etc-kubernetes"
hostPath:
path: "/etc/kubernetes"
- name: "var-lib-kubelet"
hostPath:
path: "/var/lib/kubelet"
- path: "/etc/systemd/system/kubelet.service"
permissions: "0644"
owner: "root"
content: |
[Unit]
Description=Kubelet
Requires=docker.service
After=docker.service
[Service]
Restart=always
ExecStartPre=/bin/mkdir -p /var/lib/kubelet
ExecStartPre=/bin/mount --bind /var/lib/kubelet /var/lib/kubelet
ExecStartPre=/bin/mount --make-shared /var/lib/kubelet
ExecStart=/usr/bin/docker run \
--net=host \
--pid=host \
--privileged \
--volume=/:/rootfs:ro \
--volume=/sys:/sys:ro \
--volume=/var/run:/var/run:rw \
--volume=/var/lib/docker/:/var/lib/docker:rw \
--volume=/var/lib/kubelet/:/var/lib/kubelet:shared \
--volume=/var/log/containers/:/var/log/containers:rw \
--volume=/etc/kubernetes/:/etc/kubernetes/:rw \
{{{kubernetesHyperkubeSpec}}} \
/hyperkube kubelet \
--api-servers="https://{{{masterPrivateIp}}}:443" \
--kubeconfig=/var/lib/kubelet/kubeconfig \
--address=0.0.0.0 \
--allow-privileged=true \
--enable-server \
--enable-debugging-handlers \
--config=/etc/kubernetes/manifests \
--cluster-dns={{{kubeDnsServiceIP}}} \
--cluster-domain=cluster.local \
--cloud-provider=azure \
--cloud-config=/etc/kubernetes/azure.json \
--hairpin-mode=promiscuous-bridge \
--network-plugin=kubenet \
--reconcile-cidr \
--v=2
ExecStop=/usr/bin/docker stop -t 2 kubelet
- path: "/opt/azure/containers/provision.sh"
permissions: "0744"
encoding: gzip
owner: "root"
content: !!binary |
AGENT_PROVISION_B64_GZIP_STR

Просмотреть файл

@ -0,0 +1,123 @@
#!/bin/bash
###########################################################
# START SECRET DATA - ECHO DISABLED
###########################################################
TID=$1
SID=$2
RGP=$3
LOC=$4
SUB=$5
NSG=$6
VNT=$7
RTB=$8
SVCPrincipalClientId=$9
SVCPrincipalClientSecret=${10}
CLIENTPRIVATEKEY=${11}
CLIENTKEY=/etc/kubernetes/certs/client.key
touch $CLIENTKEY
chmod 0644 $CLIENTKEY
chown root:root $CLIENTKEY
echo $CLIENTPRIVATEKEY | /usr/bin/base64 --decode > $CLIENTKEY
AZUREJSON=/etc/kubernetes/azure.json
touch $AZUREJSON
chmod 0644 $AZUREJSON
chown root:root $AZUREJSON
AZURECONTENT=$(cat <<EOF
{
"tenantId": "$TID",
"subscriptionId": "$SID",
"aadClientId": "$SVCPrincipalClientId",
"aadClientSecret": "$SVCPrincipalClientSecret",
"resourceGroup": "$RGP",
"location": "$LOC",
"subnetName": "$SUB",
"securityGroupName": "$NSG",
"vnetName": "$VNT",
"routeTableName": "$RTB"
}
EOF
)
echo "$AZURECONTENT" > $AZUREJSON
###########################################################
# END OF SECRET DATA
###########################################################
set -x
# wait for docker to be available
ensuredockerbinary()
{
dockerfound=1
for i in {1..600}; do
if [ -e /usr/bin/docker ]
then
dockerfound=0
break
fi
sleep 1
done
if [ $dockerfound -ne 0 ]
then
echo "kubectl nor docker did not install successfully"
exit 1
fi
}
ensuredockerbinary
# start all the services
/bin/systemctl restart docker
ensureDocker()
{
dockerStarted=1
for i in {1..600}; do
/usr/bin/docker ps 2>&1 | grep "daemon running"
if [ "$?" = "0" ]
then
echo "status $?"
/bin/systemctl restart docker
else
echo "docker started"
dockerStarted=0
break
fi
sleep 1
done
if [ $dockerStarted -ne 0 ]
then
echo "docker did not start"
exit 1
fi
}
ensureDocker
/bin/systemctl restart kubelet
ensureKubernetes()
{
kubernetesStarted=1
for i in {1..600}; do
/usr/bin/docker ps | grep kubelet
if [ "$?" = "0" ]
then
echo "kubernetes started"
kubernetesStarted=0
break
else
echo "kubernetes status $?"
fi
sleep 1
done
if [ $kubernetesStarted -ne 0 ]
then
echo "kubernetes did not start"
exit 1
fi
}
ensureKubernetes
echo "Install complete successfully"

Просмотреть файл

@ -0,0 +1,166 @@
{
"apiVersion": "[variables('apiVersionDefault')]",
"copy": {
"count": "[variables('{{.Name}}Count')]",
"name": "loop"
},
"dependsOn": [
{{if .IsCustomVNET}}
"[variables('nsgID')]"
{{else}}
"[variables('vnetID')]"
{{end}}
],
"location": "[variables('location')]",
"name": "[concat(variables('{{.Name}}VMNamePrefix'), 'nic-', copyIndex())]",
"properties": {
{{if .IsCustomVNET}}
"networkSecurityGroup": {
"id": "[variables('nsgID')]"
},
{{end}}
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[variables('{{.Name}}VnetSubnetID')]"
}
}
}
],
"enableIPForwarding": true
},
"type": "Microsoft.Network/networkInterfaces"
},
{
"apiVersion": "[variables('apiVersionStorage')]",
"copy": {
"count": "[variables('{{.Name}}StorageAccountsCount')]",
"name": "loop"
},
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]"
],
"location": "[variables('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 .HasDisks}}
{
"apiVersion": "[variables('apiVersionStorage')]",
"copy": {
"count": "[variables('{{.Name}}StorageAccountsCount')]",
"name": "datadiskLoop"
},
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]"
],
"location": "[variables('location')]",
"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": "[variables('location')]",
"name": "[variables('{{.Name}}AvailabilitySet')]",
"properties": {},
"type": "Microsoft.Compute/availabilitySets"
},
{
"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": "[variables('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('username')]",
"computername": "[concat(variables('{{.Name}}VMNamePrefix'), copyIndex())]",
{{GetKubernetesAgentCustomData .}}
"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": "ReadWrite",
"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"
},
{
"apiVersion": "[variables('apiVersionDefault')]",
"copy": {
"count": "[variables('{{.Name}}Count')]",
"name": "vmLoopNode"
},
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', variables('{{.Name}}VMNamePrefix'), copyIndex())]"
],
"location": "[resourceGroup().location]",
"type": "Microsoft.Compute/virtualMachines/extensions",
"name": "[concat(variables('{{.Name}}VMNamePrefix'), copyIndex(),'/cse', copyIndex())]",
"properties": {
"publisher": "Microsoft.OSTCExtensions",
"type": "CustomScriptForLinux",
"typeHandlerVersion": "1.5",
"autoUpgradeMinorVersion": true,
"settings": {},
"protectedSettings": {
"commandToExecute": "[concat('/usr/bin/nohup /bin/bash -c \"/bin/bash /opt/azure/containers/provision.sh ',variables('tenantID'),' ',variables('subscriptionId'),' ',variables('resourceGroup'),' ',variables('location'),' ',variables('subnetName'),' ',variables('nsgName'),' ',variables('virtualNetworkName'),' ',variables('routeTableName'),' {{GetAgentSecrets}} >> /var/log/azure/cluster-provision.log 2>&1 &\" &')]"
}
}
}

Просмотреть файл

@ -0,0 +1,14 @@
"{{.Name}}StorageAccountOffset": "[mul(variables('maxStorageAccountsPerAgent'),variables('{{.Name}}Index'))]",
"{{.Name}}Count": "[parameters('{{.Name}}Count')]",
"{{.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)))]",
"{{.Name}}VMNamePrefix": "[concat(variables('orchestratorName'), '-{{.Name}}-', variables('nameSuffix'), '-')]",
"{{.Name}}VMSize": "[parameters('{{.Name}}VMSize')]",
{{if .IsCustomVNET}}
"{{.Name}}VnetSubnetID": "[parameters('{{.Name}}VnetSubnetID')]",
"{{.Name}}SubnetName": "[parameters('{{.Name}}VnetSubnetID')]",
"{{.Name}}VnetParts": "[split(parameters('{{.Name}}VnetSubnetID'),'/subnets/')]",
{{else}}
"{{.Name}}VnetSubnetID": "[variables('vnetSubnetID')]",
"{{.Name}}SubnetName": "[variables('subnetName')]",
{{end}}

Просмотреть файл

@ -0,0 +1,31 @@
{
"$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" .}},
{{template "kubernetesparams.t" .}}
},
"variables": {
{{range $index, $agent := .AgentPoolProfiles}}
{{template "kubernetesagentvars.t" .}}
{{if .HasDisks}}
"{{.Name}}DataAccountName": "[concat(variables('storageAccountBaseName'), 'data{{$index}}')]",
{{end}}
"{{.Name}}Index": {{$index}},
"{{.Name}}AccountName": "[concat(variables('storageAccountBaseName'), 'agnt{{$index}}')]",
{{end}}
{{template "kubernetesmastervars.t" .}},
{{GetSizeMap}}
},
"resources": [
{{range .AgentPoolProfiles}}
{{template "kubernetesagentresources.t" .}},
{{end}}
{{template "kubernetesmasterresources.t" .}}
],
"outputs": {
{{template "masteroutputs.t" .}}
}
}

Просмотреть файл

@ -0,0 +1,288 @@
#cloud-config
# { { { variable } } }
packages:
- etcd
- jq
- traceroute
runcmd:
- /bin/bash -c "/bin/echo DAEMON_ARGS=--advertise-client-urls ""http://127.0.0.1:2379,http://{{{masterPrivateIp}}}:2379"" --listen-client-urls ""http://0.0.0.0:2379,http://0.0.0.0:4001"" | tee -a /etc/default/etcd"
- /usr/bin/curl -sSL --retry 12 --retry-delay 10 https://get.docker.com/ > /tmp/install-docker
- /bin/bash -c "/bin/bash /tmp/install-docker"
- /usr/bin/curl -sSL --retry 12 --retry-delay 10 https://storage.googleapis.com/kubernetes-release/release/v1.4.1/bin/linux/amd64/kubectl > /usr/local/bin/kubectl
- chmod +x /usr/local/bin/kubectl
write_files:
- path: "/etc/systemd/system/docker.service.d/clear_mount_propagation_flags.conf"
permissions: "0644"
owner: "root"
content: |
[Service]
MountFlags=shared
- path: "/etc/systemd/system/docker.service.d/overlay.conf"
permissions: "0644"
owner: "root"
content: |
[Service]
ExecStart=
ExecStart=/usr/bin/docker daemon -H fd:// --storage-driver=overlay
- path: "/etc/kubernetes/certs/ca.crt"
permissions: "0644"
encoding: "base64"
owner: "root"
content: |
{{{caCertificate}}}
- path: "/etc/kubernetes/certs/apiserver.crt"
permissions: "0644"
encoding: "base64"
owner: "root"
content: |
{{{apiServerCertificate}}}
- path: "/etc/kubernetes/certs/client.crt"
permissions: "0644"
encoding: "base64"
owner: "root"
content: |
{{{clientCertificate}}}
- path: "/var/lib/kubelet/kubeconfig"
permissions: "0644"
owner: "root"
content: |
apiVersion: v1
kind: Config
clusters:
- name: localcluster
cluster:
certificate-authority: /etc/kubernetes/certs/ca.crt
server: https://{{{masterPrivateIp}}}:443
users:
- name: client
user:
client-certificate: /etc/kubernetes/certs/client.crt
client-key: /etc/kubernetes/certs/client.key
contexts:
- context:
cluster: localcluster
user: client
name: localclustercontext
current-context: localclustercontext
- path: /etc/kubernetes/manifests/kube-apiserver.yaml
permissions: "0644"
owner: "root"
content: |
apiVersion: "v1"
kind: "Pod"
metadata:
name: "kube-apiserver"
namespace: "kube-system"
labels:
tier: control-plane
component: kube-apiserver
spec:
hostNetwork: true
containers:
- name: "kube-apiserver"
image: "{{{kubernetesHyperkubeSpec}}}"
command:
- "/hyperkube"
- "apiserver"
- "--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota"
- "--address=0.0.0.0"
- "--allow-privileged"
- "--insecure-port=8080"
- "--secure-port=443"
- "--cloud-provider=azure"
- "--cloud-config=/etc/kubernetes/azure.json"
- "--service-cluster-ip-range={{{kubeServiceCidr}}}"
- "--etcd-servers=http://127.0.0.1:4001"
- "--tls-cert-file=/etc/kubernetes/certs/apiserver.crt"
- "--tls-private-key-file=/etc/kubernetes/certs/apiserver.key"
- "--client-ca-file=/etc/kubernetes/certs/ca.crt"
- "--service-account-key-file=/etc/kubernetes/certs/apiserver.key"
- "--v=4"
volumeMounts:
- name: "etc-kubernetes"
mountPath: "/etc/kubernetes"
- name: "var-lib-kubelet"
mountPath: "/var/lib/kubelet"
volumes:
- name: "etc-kubernetes"
hostPath:
path: "/etc/kubernetes"
- name: "var-lib-kubelet"
hostPath:
path: "/var/lib/kubelet"
- path: /etc/kubernetes/manifests/kube-controller-manager.yaml
permissions: "0644"
owner: "root"
content: |
apiVersion: "v1"
kind: "Pod"
metadata:
name: "kube-controller-manager"
namespace: "kube-system"
labels:
tier: control-plane
component: kube-controller-manager
spec:
hostNetwork: true
containers:
- name: "kube-controller-manager"
image: "{{{kubernetesHyperkubeSpec}}}"
command:
- "/hyperkube"
- "controller-manager"
- "--master=127.0.0.1:8080"
- "--kubeconfig=/var/lib/kubelet/kubeconfig"
- "--allocate-node-cidrs=true"
- "--cluster-cidr=10.244.0.0/16"
- "--cluster-name={{{masterFqdnPrefix}}}"
- "--cloud-provider=azure"
- "--cloud-config=/etc/kubernetes/azure.json"
- "--root-ca-file=/etc/kubernetes/certs/ca.crt"
- "--service-account-private-key-file=/etc/kubernetes/certs/apiserver.key"
- "--v=2"
volumeMounts:
- name: "etc-kubernetes"
mountPath: "/etc/kubernetes"
- name: "var-lib-kubelet"
mountPath: "/var/lib/kubelet"
volumes:
- name: "etc-kubernetes"
hostPath:
path: "/etc/kubernetes"
- name: "var-lib-kubelet"
hostPath:
path: "/var/lib/kubelet"
- path: /etc/kubernetes/manifests/kube-scheduler.yaml
permissions: "0644"
owner: "root"
content: |
apiVersion: "v1"
kind: "Pod"
metadata:
name: "kube-scheduler"
namespace: "kube-system"
labels:
tier: control-plane
component: kube-scheduler
spec:
hostNetwork: true
containers:
- name: "kube-scheduler"
image: "{{{kubernetesHyperkubeSpec}}}"
command:
- "/hyperkube"
- "scheduler"
- "--master=127.0.0.1:8080"
- "--kubeconfig=/var/lib/kubelet/kubeconfig"
- "--v=2"
volumeMounts:
- name: "etc-kubernetes"
mountPath: "/etc/kubernetes"
- name: "var-lib-kubelet"
mountPath: "/var/lib/kubelet"
volumes:
- name: "etc-kubernetes"
hostPath:
path: "/etc/kubernetes"
- name: "var-lib-kubelet"
hostPath:
path: "/var/lib/kubelet"
- path: /etc/kubernetes/manifests/kube-proxy.yaml
permissions: "0644"
owner: "root"
content: |
apiVersion: "v1"
kind: "Pod"
metadata:
name: "kube-proxy"
namespace: "kube-system"
labels:
tier: control-plane
component: kube-proxy
spec:
hostNetwork: true
containers:
- name: "kube-proxy"
image: "{{{kubernetesHyperkubeSpec}}}"
command:
- "/hyperkube"
- "proxy"
- "--kubeconfig=/var/lib/kubelet/kubeconfig"
- "--proxy-mode=iptables"
- "--v=2"
securityContext:
privileged: true
volumeMounts:
- name: "etc-kubernetes"
mountPath: "/etc/kubernetes"
- name: "var-lib-kubelet"
mountPath: "/var/lib/kubelet"
volumes:
- name: "etc-kubernetes"
hostPath:
path: "/etc/kubernetes"
- name: "var-lib-kubelet"
hostPath:
path: "/var/lib/kubelet"
- path: "/etc/systemd/system/kubelet.service"
permissions: "0644"
owner: "root"
content: |
[Unit]
Description=Kubelet
Requires=docker.service
After=docker.service
[Service]
Restart=always
ExecStartPre=/bin/mkdir -p /var/lib/kubelet
ExecStartPre=/bin/mount --bind /var/lib/kubelet /var/lib/kubelet
ExecStartPre=/bin/mount --make-shared /var/lib/kubelet
ExecStart=/usr/bin/docker run \
--net=host \
--pid=host \
--privileged \
--volume=/:/rootfs:ro \
--volume=/sys:/sys:ro \
--volume=/var/run:/var/run:rw \
--volume=/var/lib/docker/:/var/lib/docker:rw \
--volume=/var/lib/kubelet/:/var/lib/kubelet:shared \
--volume=/var/log/containers/:/var/log/containers:rw \
--volume=/etc/kubernetes/:/etc/kubernetes:rw \
{{{kubernetesHyperkubeSpec}}} \
/hyperkube kubelet \
--api-servers="https://{{{masterPrivateIp}}}:443" \
--kubeconfig=/var/lib/kubelet/kubeconfig \
--address=0.0.0.0 \
--allow-privileged=true \
--enable-server \
--enable-debugging-handlers \
--config=/etc/kubernetes/manifests \
--cluster-dns={{{kubeDnsServiceIP}}} \
--cluster-domain=cluster.local \
--register-schedulable=false \
--cloud-provider=azure \
--cloud-config=/etc/kubernetes/azure.json \
--v=2
ExecStop=/usr/bin/docker stop -t 2 kubelet
- path: "/opt/azure/containers/provision.sh"
permissions: "0744"
encoding: gzip
owner: "root"
content: !!binary |
MASTER_PROVISION_B64_GZIP_STR

Просмотреть файл

@ -0,0 +1,143 @@
#!/bin/bash
###########################################################
# START SECRET DATA - ECHO DISABLED
###########################################################
TID=$1
SID=$2
RGP=$3
LOC=$4
SUB=$5
NSG=$6
VNT=$7
RTB=$8
SVCPrincipalClientId=$9
SVCPrincipalClientSecret=${10}
CLIENTPRIVATEKEY=${11}
SERVERPRIVATEKEY=${12}
APISERVERKEY=/etc/kubernetes/certs/apiserver.key
touch $APISERVERKEY
chmod 0644 $APISERVERKEY
chown root:root $APISERVERKEY
echo $SERVERPRIVATEKEY | /usr/bin/base64 --decode > $APISERVERKEY
CLIENTKEY=/etc/kubernetes/certs/client.key
touch $CLIENTKEY
chmod 0644 $CLIENTKEY
chown root:root $CLIENTKEY
echo $CLIENTPRIVATEKEY | /usr/bin/base64 --decode > $CLIENTKEY
AZUREJSON=/etc/kubernetes/azure.json
touch $AZUREJSON
chmod 0644 $AZUREJSON
chown root:root $AZUREJSON
AZURECONTENT=$(cat <<EOF
{
"tenantId": "$TID",
"subscriptionId": "$SID",
"aadClientId": "$SVCPrincipalClientId",
"aadClientSecret": "$SVCPrincipalClientSecret",
"resourceGroup": "$RGP",
"location": "$LOC",
"subnetName": "$SUB",
"securityGroupName": "$NSG",
"vnetName": "$VNT",
"routeTableName": "$RTB"
}
EOF
)
echo "$AZURECONTENT" > $AZUREJSON
###########################################################
# END OF SECRET DATA
###########################################################
set -x
# wait for kubectl to report successful cluster health
ensurekubectl()
{
kubectlfound=1
for i in {1..600}; do
if [ -e /usr/local/bin/kubectl ]
then
kubectlfound=0
break
fi
sleep 1
done
if [ $kubectlfound -ne 0 ]
then
if [ ! -e /usr/bin/docker ]
then
echo "kubectl nor docker did not install successfully"
exit 1
fi
fi
}
ensurekubectl
/bin/systemctl restart etcd
# start all the services
/bin/systemctl restart docker
ensureDocker()
{
dockerStarted=1
for i in {1..600}; do
/usr/bin/docker ps 2>&1 | grep "daemon running"
if [ "$?" = "0" ]
then
echo "status $?"
/bin/systemctl restart docker
else
echo "docker started"
dockerStarted=0
break
fi
sleep 1
done
if [ $dockerStarted -ne 0 ]
then
echo "docker did not start"
exit 1
fi
}
ensureDocker
/bin/systemctl restart kubelet
#wait for kubernetes to start
ensureKubernetes()
{
kubernetesStarted=1
for i in {1..600}; do
if [ -e /usr/local/bin/kubectl ]
then
/usr/local/bin/kubectl cluster-info
if [ "$?" = "0" ]
then
echo "kubernetes started"
kubernetesStarted=0
break
fi
else
/usr/bin/docker ps | grep apiserver
if [ "$?" = "0" ]
then
echo "kubernetes started"
kubernetesStarted=0
break
fi
fi
sleep 1
done
if [ $kubernetesStarted -ne 0 ]
then
echo "kubernetes did not start"
exit 1
fi
}
ensureKubernetes
echo "Install complete successfully"

Просмотреть файл

@ -0,0 +1,323 @@
{
"apiVersion": "[variables('apiVersionDefault')]",
"location": "[variables('location')]",
"name": "[variables('masterAvailabilitySet')]",
"properties": {},
"type": "Microsoft.Compute/availabilitySets"
},
{
"apiVersion": "[variables('apiVersionStorage')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]"
],
"location": "[variables('location')]",
"name": "[variables('masterStorageAccountName')]",
"properties": {
"accountType": "[variables('vmSizesMap')[variables('masterVMSize')].storageAccountType]"
},
"type": "Microsoft.Storage/storageAccounts"
},
{{if not .MasterProfile.IsCustomVNET}}
{
"apiVersion": "[variables('apiVersionDefault')]",
"dependsOn": [
"[concat('Microsoft.Network/networkSecurityGroups/', variables('nsgName'))]",
"[concat('Microsoft.Network/routeTables/', variables('routeTableName'))]"
],
"location": "[variables('location')]",
"name": "[variables('virtualNetworkName')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('vnetCidr')]"
]
},
"subnets": [
{
"name": "[variables('subnetName')]",
"properties": {
"addressPrefix": "[variables('subnet')]",
"networkSecurityGroup": {
"id": "[variables('nsgID')]"
},
"routeTable": {
"id": "[variables('routeTableID')]"
}
}
}
]
},
"type": "Microsoft.Network/virtualNetworks"
},
{{end}}
{
"apiVersion": "[variables('apiVersionDefault')]",
"location": "[variables('location')]",
"name": "[variables('nsgName')]",
"properties": {
"securityRules": [
{
"name": "allow_ssh",
"properties": {
"access": "Allow",
"description": "Allow SSH traffic to master",
"destinationAddressPrefix": "*",
"destinationPortRange": "22-22",
"direction": "Inbound",
"priority": 101,
"protocol": "Tcp",
"sourceAddressPrefix": "*",
"sourcePortRange": "*"
}
},
{
"name": "allow_kube_tls",
"properties": {
"access": "Allow",
"description": "Allow kube-apiserver (tls) traffic to master",
"destinationAddressPrefix": "*",
"destinationPortRange": "443-443",
"direction": "Inbound",
"priority": 100,
"protocol": "Tcp",
"sourceAddressPrefix": "*",
"sourcePortRange": "*"
}
}
]
},
"type": "Microsoft.Network/networkSecurityGroups"
},
{
"apiVersion": "[variables('apiVersionDefault')]",
"location": "[variables('location')]",
"name": "[variables('routeTableName')]",
"type": "Microsoft.Network/routeTables"
},
{
"apiVersion": "[variables('apiVersionDefault')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]"
],
"location": "[variables('location')]",
"name": "[variables('masterLbName')]",
"properties": {
"backendAddressPools": [
{
"name": "[variables('masterLbBackendPoolName')]"
}
],
"frontendIPConfigurations": [
{
"name": "[variables('masterLbIPConfigName')]",
"properties": {
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses',variables('masterPublicIPAddressName'))]"
}
}
}
],
"loadBalancingRules": [
{
"name": "LBRuleHTTPS",
"properties": {
"frontendIPConfiguration": {
"id": "[variables('masterLbIPConfigID')]"
},
"backendAddressPool": {
"id": "[concat(variables('masterLbID'), '/backendAddressPools/', variables('masterLbBackendPoolName'))]"
},
"protocol": "tcp",
"frontendPort": 443,
"backendPort": 443,
"enableFloatingIP": false,
"idleTimeoutInMinutes": 5,
"loadDistribution": "Default",
"probe": {
"id": "[concat(variables('masterLbID'),'/probes/tcpHTTPSProbe')]"
}
}
}
],
"probes": [
{
"name": "tcpHTTPSProbe",
"properties": {
"protocol": "tcp",
"port": 443,
"intervalInSeconds": "5",
"numberOfProbes": "2"
}
}
]
},
"type": "Microsoft.Network/loadBalancers"
},
{
"apiVersion": "[variables('apiVersionDefault')]",
"location": "[variables('location')]",
"name": "[variables('masterPublicIPAddressName')]",
"properties": {
"dnsSettings": {
"domainNameLabel": "[variables('masterFqdnPrefix')]"
},
"publicIPAllocationMethod": "Dynamic"
},
"type": "Microsoft.Network/publicIPAddresses"
},
{
"apiVersion": "[variables('apiVersionDefault')]",
"copy": {
"count": "[variables('masterCount')]",
"name": "masterLbLoopNode"
},
"dependsOn": [
"[variables('masterLbID')]"
],
"location": "[variables('location')]",
"name": "[concat(variables('masterLbName'), '/', 'SSH-', variables('masterVMNamePrefix'), copyIndex())]",
"properties": {
"backendPort": 22,
"enableFloatingIP": false,
"frontendIPConfiguration": {
"id": "[variables('masterLbIPConfigID')]"
},
"frontendPort": "[variables('sshNatPorts')[copyIndex()]]",
"protocol": "tcp"
},
"type": "Microsoft.Network/loadBalancers/inboundNatRules"
},
{
"apiVersion": "[variables('apiVersionDefault')]",
"copy": {
"count": "[variables('masterCount')]",
"name": "nicLoopNode"
},
"dependsOn": [
{{if .MasterProfile.IsCustomVNET}}
"[variables('nsgID')]",
{{else}}
"[variables('vnetID')]",
{{end}}
"[concat(variables('masterLbID'),'/inboundNatRules/SSH-',variables('masterVMNamePrefix'),copyIndex())]",
"[variables('nsgID')]"
],
"location": "[variables('location')]",
"name": "[concat(variables('masterVMNamePrefix'), 'nic-', copyIndex())]",
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"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('vnetSubnetID')]"
}
}
}
],
"enableIPForwarding": true
{{if .MasterProfile.IsCustomVNET}}
,"networkSecurityGroup": {
"id": "[variables('nsgID')]"
}
{{end}}
},
"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')]"
],
"location": "[variables('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('username')]",
"computername": "[concat(variables('masterVMNamePrefix'), copyIndex())]",
{{GetKubernetesMasterCustomData}}
"linuxConfiguration": {
"disablePasswordAuthentication": "true",
"ssh": {
"publicKeys": [
{
"keyData": "[variables('sshPublicKeyData')]",
"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')]",
"copy": {
"count": "[variables('masterCount')]",
"name": "vmLoopNode"
},
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', variables('masterVMNamePrefix'), copyIndex())]"
],
"location": "[resourceGroup().location]",
"type": "Microsoft.Compute/virtualMachines/extensions",
"name": "[concat(variables('masterVMNamePrefix'), copyIndex(),'/cse', copyIndex())]",
"properties": {
"publisher": "Microsoft.OSTCExtensions",
"type": "CustomScriptForLinux",
"typeHandlerVersion": "1.5",
"autoUpgradeMinorVersion": true,
"settings": {},
"protectedSettings": {
"commandToExecute": "[concat('/usr/bin/nohup /bin/bash -c \"/bin/bash /opt/azure/containers/provision.sh ',variables('tenantID'),' ',variables('subscriptionId'),' ',variables('resourceGroup'),' ',variables('location'),' ',variables('subnetName'),' ',variables('nsgName'),' ',variables('virtualNetworkName'),' ',variables('routeTableName'),' {{GetMasterSecrets}} >> /var/log/azure/cluster-provision.log 2>&1 &\" &')]"
}
}
}

Просмотреть файл

@ -0,0 +1,77 @@
"maxVMsPerPool": 100,
"maxVMsPerStorageAccount": 20,
"maxStorageAccountsPerAgent": "[div(variables('maxVMsPerPool'),variables('maxVMsPerStorageAccount'))]",
"apiServerCertificate": "[parameters('apiServerCertificate')]",
"apiServerPrivateKey": "[parameters('apiServerPrivateKey')]",
"caCertificate": "[parameters('caCertificate')]",
"clientCertificate": "[parameters('clientCertificate')]",
"clientPrivateKey": "[parameters('clientPrivateKey')]",
"kubeConfigCertificate": "[parameters('kubeConfigCertificate')]",
"kubeConfigPrivateKey": "[parameters('kubeConfigPrivateKey')]",
"kubernetesHyperkubeSpec": "[parameters('kubernetesHyperkubeSpec')]",
"servicePrincipalClientId": "[parameters('servicePrincipalClientId')]",
"servicePrincipalClientSecret": "[parameters('servicePrincipalClientSecret')]",
"username": "[parameters('linuxAdminUsername')]",
"masterFqdnPrefix": "[parameters('masterEndpointDNSNamePrefix')]",
"masterPrivateIp": "[parameters('firstConsecutiveStaticIP')]",
"masterVMSize": "[parameters('masterVMSize')]",
"sshPublicKeyData": "[parameters('sshRSAPublicKey')]",
"masterCount": {{.MasterProfile.Count}},
"apiVersionDefault": "2016-03-30",
"apiVersionStorage": "2015-06-15",
"location": "[resourceGroup().location]",
"masterAvailabilitySet": "master-availabilityset",
"storageAccountBaseName": "[uniqueString(concat(variables('masterFqdnPrefix'),resourceGroup().location, variables('orchestratorName')))]",
"masterStorageAccountName": "[concat(variables('storageAccountBaseName'), 'mstr0')]",
"nameSuffix": "[parameters('nameSuffix')]",
"orchestratorName": "k8s",
"osImageOffer": "UbuntuServer",
"osImagePublisher": "Canonical",
"osImageSKU": "16.04.0-LTS",
"osImageVersion": "16.04.201606270",
"resourceGroup": "[resourceGroup().name]",
"routeTableName": "[concat(variables('masterVMNamePrefix'),'routetable')]",
"routeTableID": "[resourceId('Microsoft.Network/routeTables', variables('routeTableName'))]",
"sshNatPorts": [22,2201,2202,2203,2204],
"sshKeyPath": "[concat('/home/',variables('username'),'/.ssh/authorized_keys')]",
"storageAccountBaseName": "[uniqueString(concat(variables('masterFqdnPrefix'),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'))]",
"vmsPerStorageAccount": 20,
{{if AnyAgentHasDisks}}
"dataStorageAccountPrefixSeed": 97,
{{end}}
{{if .MasterProfile.IsCustomVNET}}
"vnetSubnetID": "[parameters('masterVnetSubnetID')]",
"subnetName": "[parameters('masterVnetSubnetID')]",
"vnetParts": "[split(parameters('masterVnetSubnetID'),'/subnets/')]",
"virtualNetworkName": "[variables('vnetParts')[0]]",
{{else}}
"subnet": "[parameters('masterSubnet')]",
"subnetName": "[concat(variables('orchestratorName'), '-subnet')]",
"vnetID": "[resourceId('Microsoft.Network/virtualNetworks',variables('virtualNetworkName'))]",
"vnetSubnetID": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "[concat(variables('orchestratorName'), '-vnet-', variables('nameSuffix'))]",
"vnetCidr": "10.0.0.0/8",
{{end}}
"kubeDnsServiceIp": "10.0.0.10",
"kubeServiceCidr": "10.0.0.0/16",
"nsgName": "[concat(variables('masterVMNamePrefix'), 'nsg')]",
"nsgID": "[resourceId('Microsoft.Network/networkSecurityGroups',variables('nsgName'))]",
"masterPublicIPAddressName": "[concat(variables('orchestratorName'), '-master-ip-', variables('masterFqdnPrefix'), '-', 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'))]",
"masterLbBackendPoolName": "[concat(variables('orchestratorName'), '-master-pool-', variables('nameSuffix'))]",
"masterFirstAddrComment": "these MasterFirstAddrComment are used to place multiple masters consecutively in the address space",
"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'), '-')]",
"subscriptionId": "[subscription().subscriptionId]",
"tenantId": "[subscription().tenantId]"

Просмотреть файл

@ -0,0 +1,60 @@
"apiServerCertificate": {
"metadata": {
"description": "The base 64 server certificate used on the master"
},
"type": "string"
},
"apiServerPrivateKey": {
"metadata": {
"description": "The base 64 server private key used on the master."
},
"type": "securestring"
},
"caCertificate": {
"metadata": {
"description": "The base 64 certificate authority certificate"
},
"type": "string"
},
"clientCertificate": {
"metadata": {
"description": "The base 64 client certificate used to communicate with the master"
},
"type": "string"
},
"clientPrivateKey": {
"metadata": {
"description": "The base 64 client private key used to communicate with the master"
},
"type": "securestring"
},
"kubeConfigCertificate": {
"metadata": {
"description": "The base 64 certificate used by cli to communicate with the master"
},
"type": "string"
},
"kubeConfigPrivateKey": {
"metadata": {
"description": "The base 64 private key used by cli to communicate with the master"
},
"type": "securestring"
},
"kubernetesHyperkubeSpec": {
"metadata": {
"description": "The container spec for hyperkube."
},
"type": "string"
},
"servicePrincipalClientId": {
"metadata": {
"description": "Client ID (used by cloudprovider)"
},
"type": "securestring"
},
"servicePrincipalClientSecret": {
"metadata": {
"description": "The Service Principal Client Secret."
},
"type": "securestring"
}

Просмотреть файл

@ -2,3 +2,19 @@
"type": "string",
"value": "[reference(concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))).dnsSettings.fqdn]"
}
{{if .GetClassicMode}}
{{if RequiresFakeAgentOutput}}
,"agentFQDN": {
"type": "string",
"value": ""
},
{{end}}
"diagnosticsStorageAccountUri": {
"type": "string",
"value": ""
},
"jumpboxFQDN": {
"type": "string",
"value": ""
}
{{end}}

Просмотреть файл

@ -1,12 +1,10 @@
"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."
},
@ -14,7 +12,6 @@
},
{{if .MasterProfile.IsCustomVNET}}
"masterVnetSubnetID": {
"defaultValue": "{{.MasterProfile.VnetSubnetID}}",
"metadata": {
"description": "Sets the vnet subnet of the master."
},
@ -24,7 +21,7 @@
"masterSubnet": {
"defaultValue": "{{.MasterProfile.GetSubnet}}",
"metadata": {
"description": "Sets the subnet of the master."
"description": "Sets the subnet of the master node(s)."
},
"type": "string"
},
@ -37,65 +34,25 @@
"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}}",
{{GetMasterAllowedSizes}}
"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"
}
},
"nameSuffix": {
"defaultValue": "{{GetUniqueNameSuffix}}",
"metadata": {
"description": "A string hash of the master DNS name to uniquely identify the cluster."
},
"type": "string"
}
{{if .GetClassicMode}}
,{{template "classicparams.t" .}}
{{end}}

Просмотреть файл

@ -1,7 +1,7 @@
{
"apiVersion": "[variables('storageApiVersion')]",
"apiVersion": "[variables('apiVersionStorage')]",
"copy": {
"count": "[variables('agentStorageAccountsCount')]",
"count": "[variables('{{.Name}}StorageAccountsCount')]",
"name": "vmLoopNode"
},
"dependsOn": [
@ -16,7 +16,7 @@
},
{{if IsPublic .Ports}}
{
"apiVersion": "[variables('networkApiVersion')]",
"apiVersion": "[variables('apiVersionDefault')]",
"location": "[resourceGroup().location]",
"name": "[variables('{{.Name}}IPAddressName')]",
"properties": {
@ -28,7 +28,7 @@
"type": "Microsoft.Network/publicIPAddresses"
},
{
"apiVersion": "[variables('networkApiVersion')]",
"apiVersion": "[variables('apiVersionDefault')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('{{.Name}}IPAddressName'))]"
],
@ -62,16 +62,18 @@
},
{{end}}
{
"apiVersion": "[variables('computeApiVersion')]",
"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'))]",
"[variables('vnetID')]"
{{if IsPublic .Ports}},
"[variables('{{.Name}}LbID')]"
"[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 not .IsCustomVNET}}
,"[variables('vnetID')]"
{{end}}
{{if IsPublic .Ports}}
,"[variables('{{.Name}}LbID')]"
{{end}}
],
"location": "[resourceGroup().location]",
@ -98,7 +100,7 @@
],
{{end}}
"subnet": {
"id": "[variables('{{.Name}}SubnetRef')]"
"id": "[variables('{{.Name}}VnetSubnetID')]"
}
}
}
@ -136,11 +138,11 @@
"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')]"
"[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')]"
]
}
}

Просмотреть файл

@ -0,0 +1,192 @@
{
"apiVersion": "[variables('apiVersionDefault')]",
"copy": {
"count": "[variables('{{.Name}}Count')]",
"name": "loop"
},
"dependsOn": [
{{if not .IsCustomVNET}}
"[variables('vnetID')]"
{{end}}
{{if IsPublic .Ports}}
,"[variables('{{.Name}}LbID')]"
{{end}}
],
"location": "[resourceGroup().location]",
"name": "[concat(variables('{{.Name}}VMNamePrefix'), 'nic-', copyIndex())]",
"properties": {
"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": "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 .HasDisks}}
{
"apiVersion": "[variables('apiVersionStorage')]",
"copy": {
"count": "[variables('{{.Name}}StorageAccountsCount')]",
"name": "datadiskLoop"
},
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]"
],
"location": "[resourceGroup().location]",
"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({{template "swarmagentcustomdata.t" .}})]",
"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"
}

Просмотреть файл

@ -1,10 +1,20 @@
"{{.Name}}StorageAccountOffset": "[mul(variables('agentStorageAccountsCount'),variables('{{.Name}}Index'))]",
"{{.Name}}StorageAccountOffset": "[mul(variables('maxStorageAccountsPerAgent'),variables('{{.Name}}Index'))]",
"{{.Name}}Count": "[parameters('{{.Name}}Count')]",
{{if .IsVolumeBasedStorage}}
"{{.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}}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}}SubnetRef": "[concat(variables('vnetID'),'/subnets/',variables('{{.Name}}SubnetName'))]",
"{{.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'))]",
@ -13,4 +23,8 @@
"{{.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'))]",
{{if .IsWindows}}
"{{.Name}}WindowsRDPNatRangeStart": 3389,
"{{.Name}}WindowsRDPEndRangeStop": "[add(variables('{{.Name}}WindowsRDPNatRangeStart'), add(variables('{{.Name}}Count'),variables('{{.Name}}Count')))]",
{{end}}
{{end}}

Просмотреть файл

@ -3,19 +3,55 @@
"contentVersion": "1.0.0.0",
"parameters": {
{{range .AgentPoolProfiles}}{{template "agentparams.t" .}},{{end}}
{{if .HasWindows}}
{{template "windowsparams.t"}},
{{end}}
{{template "masterparams.t" .}}
},
"variables": {
{{range $index, $agent := .AgentPoolProfiles}}
{{template "swarmagentvars.t" .}}
{{if .IsVolumeBasedStorage}}
"{{.Name}}DataAccountName": "[concat(variables('storageAccountBaseName'), 'data{{$index}}')]",
{{end}}
"{{.Name}}Index": {{$index}},
"{{.Name}}AccountName": "[concat(variables('storageAccountBaseName'), 'agnt{{$index}}')]",
{{end}}
{{template "swarmmastervars.t" .}}
{{if .HasWindows}}
"windowsAdminUsername": "[parameters('windowsAdminUsername')]",
"windowsAdminPassword": "[parameters('windowsAdminPassword')]",
"agentWindowsPublisher": "MicrosoftWindowsServer",
"agentWindowsOffer": "WindowsServer",
"agentWindowsSku": "2016-Technical-Preview-with-Containers",
"agentWindowsVersion": "5.0.20160803",
"singleQuote": "'",
"windowsCustomScriptArguments": "[concat('$arguments = ', variables('singleQuote'),'-SwarmMasterIP ', variables('masterFirstAddrPrefix'), variables('masterFirstAddrOctet4'), variables('singleQuote'), ' ; ')]",
"windowsCustomScriptSuffix": " $inputFile = '%SYSTEMDRIVE%\\AzureData\\CustomData.bin' ; $outputFile = '%SYSTEMDRIVE%\\AzureData\\CustomDataSetupScript.ps1' ; $inputStream = New-Object System.IO.FileStream $inputFile, ([IO.FileMode]::Open), ([IO.FileAccess]::Read), ([IO.FileShare]::Read) ; $sr = New-Object System.IO.StreamReader(New-Object System.IO.Compression.GZipStream($inputStream, [System.IO.Compression.CompressionMode]::Decompress)) ; $sr.ReadToEnd() | Out-File($outputFile) ; Invoke-Expression('{0} {1}' -f $outputFile, $arguments) ; ",
"windowsCustomScript": "[concat('powershell.exe -ExecutionPolicy Unrestricted -command \"', variables('windowsCustomScriptArguments'), variables('windowsCustomScriptSuffix'), '\" > %SYSTEMDRIVE%\\AzureData\\CustomDataSetupScript.log 2>&1')]",
"agentWindowsBackendPort": 3389,
{{end}}
{{template "swarmmastervars.t" .}},
{{GetSizeMap}}
},
"resources": [
{{range .AgentPoolProfiles}}{{template "swarmagentresources.t" .}},{{end}}
{{range .AgentPoolProfiles}}
{{if .IsWindows}}
{{if .IsVolumeBasedStorage}}
{{template "swarmwinagentresourcesdisks.t" .}},
{{else}}
{{template "swarmwinagentresources.t" .}},
{{end}}
{{else}}
{{if .IsVolumeBasedStorage}}
{{template "swarmagentresourcesdisks.t" .}},
{{else}}
{{template "swarmagentresources.t" .}},
{{end}}
{{end}}
{{end}}
{{template "swarmmasterresources.t" .}}
],
"outputs": {

Просмотреть файл

@ -1,5 +1,5 @@
{
"apiVersion": "[variables('storageApiVersion')]",
"apiVersion": "[variables('apiVersionStorage')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]"
],
@ -9,9 +9,10 @@
"accountType": "[variables('vmSizesMap')[variables('masterVMSize')].storageAccountType]"
},
"type": "Microsoft.Storage/storageAccounts"
},
},
{{if not .MasterProfile.IsCustomVNET}}
{
"apiVersion": "[variables('networkApiVersion')]",
"apiVersion": "[variables('apiVersionDefault')]",
"location": "[resourceGroup().location]",
"name": "[variables('virtualNetworkName')]",
"properties": {
@ -25,16 +26,17 @@
]
},
"type": "Microsoft.Network/virtualNetworks"
},
},
{{end}}
{
"apiVersion": "[variables('computeApiVersion')]",
"apiVersion": "[variables('apiVersionDefault')]",
"location": "[resourceGroup().location]",
"name": "[variables('masterAvailabilitySet')]",
"properties": {},
"type": "Microsoft.Compute/availabilitySets"
},
{
"apiVersion": "[variables('networkApiVersion')]",
"apiVersion": "[variables('apiVersionDefault')]",
"location": "[resourceGroup().location]",
"name": "[variables('masterPublicIPAddressName')]",
"properties": {
@ -46,7 +48,7 @@
"type": "Microsoft.Network/publicIPAddresses"
},
{
"apiVersion": "[variables('networkApiVersion')]",
"apiVersion": "[variables('apiVersionDefault')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))]"
],
@ -72,7 +74,7 @@
"type": "Microsoft.Network/loadBalancers"
},
{
"apiVersion": "[variables('networkApiVersion')]",
"apiVersion": "[variables('apiVersionDefault')]",
"copy": {
"count": "[variables('masterCount')]",
"name": "masterLbLoopNode"
@ -94,14 +96,16 @@
"type": "Microsoft.Network/loadBalancers/inboundNatRules"
},
{
"apiVersion": "[variables('networkApiVersion')]",
"apiVersion": "[variables('apiVersionDefault')]",
"copy": {
"count": "[variables('masterCount')]",
"name": "nicLoopNode"
},
"dependsOn": [
"[variables('masterLbID')]",
{{if not .MasterProfile.IsCustomVNET}}
"[variables('vnetID')]",
{{end}}
"[variables('masterLbID')]",
"[concat(variables('masterLbID'),'/inboundNatRules/SSH-',variables('masterVMNamePrefix'),copyIndex())]"
],
"location": "[resourceGroup().location]",
@ -124,7 +128,7 @@
"privateIPAddress": "[concat(variables('masterFirstAddrPrefix'), copyIndex(int(variables('masterFirstAddrOctet4'))))]",
"privateIPAllocationMethod": "Static",
"subnet": {
"id": "[variables('masterSubnetRef')]"
"id": "[variables('masterVnetSubnetID')]"
}
}
}
@ -133,7 +137,7 @@
"type": "Microsoft.Network/networkInterfaces"
},
{
"apiVersion": "[variables('computeApiVersion')]",
"apiVersion": "[variables('apiVersionDefault')]",
"copy": {
"count": "[variables('masterCount')]",
"name": "vmLoopNode"
@ -187,7 +191,7 @@
"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')]"
"uri": "[concat(reference(concat('Microsoft.Storage/storageAccounts/', variables('masterStorageAccountName')), variables('apiVersionStorage')).primaryEndpoints.blob, 'vhds/', variables('masterVMNamePrefix'), copyIndex(), '-osdisk.vhd')]"
}
}
}
@ -195,7 +199,7 @@
"type": "Microsoft.Compute/virtualMachines"
},
{
"apiVersion": "[variables('computeApiVersion')]",
"apiVersion": "[variables('apiVersionDefault')]",
"copy": {
"count": "[variables('masterCount')]",
"name": "vmLoopNode"

Просмотреть файл

@ -1,16 +1,19 @@
"adminUsername": "[parameters('linuxAdminUsername')]",
"agentStorageAccountsCount": 5,
"maxVMsPerPool": 100,
"maxVMsPerStorageAccount": 20,
"maxStorageAccountsPerAgent": "[div(variables('maxVMsPerPool'),variables('maxVMsPerStorageAccount'))]",
"dataStorageAccountPrefixSeed": 97,
"apiVersionDefault": "2016-03-30",
"apiVersionStorage": "2015-06-15",
"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')]",
"agentMaxVMs": 100,
"clusterInstallParameters": "[concat(variables('masterCount'), ' ',variables('masterVMNamePrefix'), ' ',variables('masterFirstAddrOctet4'), ' ',variables('adminUsername'),' ',variables('postInstallScriptURI'),' ',variables('masterFirstAddrPrefix'))]",
"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'))]",
@ -18,15 +21,21 @@
"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'))]",
{{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'), '-')]",
"masterVMSize": "[parameters('masterVMSize')]",
"nameSuffix": "{{GetUniqueNameSuffix}}",
"networkApiVersion": "2016-03-30",
"nameSuffix": "[parameters('nameSuffix')]",
"orchestratorName": "swarm",
"osImageOffer": "UbuntuServer",
"osImagePublisher": "Canonical",
@ -35,191 +44,8 @@
"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"
],
"storageAccountBaseName": "[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'))]"
"vmsPerStorageAccount": 20

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -0,0 +1,12 @@
"windowsAdminUsername": {
"type": "string",
"metadata": {
"description": "User name for the Windows Swarm Agent Virtual Machines (Password Only Supported)."
}
},
"windowsAdminPassword": {
"type": "securestring",
"metadata": {
"description": "Password for the Windows Swarm Agent Virtual Machines."
}
}

Просмотреть файл

@ -0,0 +1,254 @@
<#
.SYNOPSIS
Autogenerates the allowed sizes for masters and agents and the
associated storage map.
.DESCRIPTION
Autogenerates the allowed sizes for masters and agents and the
associated storage map.
.PARAMETER OutFile
The name of the outputfile (Default is azureconst.go)
.EXAMPLE
.\Get-AzureConstants.ps1 -OutFile "azureconst.go"
#>
[CmdletBinding(DefaultParameterSetName="Standard")]
param(
[string]
$OutFile = "azureconst.go"
)
function
Get-AllSizes() {
$locations = Get-AzureRmLocation | Select-Object -Property Location
$sizeMap = @{}
ForEach ($location in $locations) {
#Write-Output $location.Location
$sizes = Get-AzureRmVMSize -Location $location.Location
ForEach ($size in $sizes) {
if (!$sizeMap.ContainsKey($size.Name)) {
$sizeMap.Add($size.Name, $size)
}
}
#break
}
return $sizeMap
}
# 1. Agents >= 2 cores
# 2. Masters >= 2 cores and ephemeral disk >= 100 GB
$MINIMUM_CORES = 2
$MASTERS_EPHEMERAL_DISK_MIN = 102400
function
Get-MasterMap() {
param(
[System.Collections.Hashtable]
$SizeMap
)
$masterMap = @{}
ForEach ($k in ($SizeMap.Keys | Sort-Object)) {
#Write-Output $location.Location
$size = $SizeMap[$k]
if ($size.NumberOfCores -ge $MINIMUM_CORES -and
$size.ResourceDiskSizeInMB -ge $MASTERS_EPHEMERAL_DISK_MIN) {
$masterMap.Add($size.Name, $size)
}
}
return $masterMap
}
function
Get-AgentMap() {
param(
[System.Collections.Hashtable]
$SizeMap
)
$agentMap = @{}
ForEach ($k in ($SizeMap.Keys | Sort-Object)) {
#Write-Output $location.Location
$size = $SizeMap[$k]
if ($size.NumberOfCores -ge $MINIMUM_CORES) {
$agentMap.Add($size.Name, $size)
}
}
return $agentMap
}
function
Get-Locations() {
$locations = Get-AzureRmLocation | Select-Object -Property Location
$locationList = @()
ForEach ($location in $locations) {
$locationList += $location.Location
}
return $locationList
}
function
Get-FileContents() {
param(
[System.Collections.Hashtable]
$MasterMap,
[System.Collections.Hashtable]
$AgentMap,
[System.Collections.ArrayList]
$Locations
)
$text = "package tgen"
$text += @"
import "fmt"
// AUTOGENERATED FILE - last generated $(Get-Date -format 'u')
const (
// AzureProdFQDNFormat specifies the format for a prod dns name
AzureProdFQDNFormat = "%s.%s.cloudapp.azure.com"
)
// AzureLocations provides all azure regions in prod.
// Related powershell to refresh this list:
// Get-AzureRmLocation | Select-Object -Property Location
var AzureLocations = []string{
"@
ForEach ($location in ($Locations | Sort-Object)) {
$text += ' "' + $location + '"' + ",`r`n"
}
$text += @"
}
// FormatAzureProdFQDNs constructs all possible Azure prod fqdn
func FormatAzureProdFQDNs(fqdnPrefix string) []string {
var fqdns []string
for _, location := range AzureLocations {
fqdns = append(fqdns, FormatAzureProdFQDN(fqdnPrefix, location))
}
return fqdns
}
// FormatAzureProdFQDN constructs an Azure prod fqdn
func FormatAzureProdFQDN(fqdnPrefix string, location string) string {
return fmt.Sprintf(AzureProdFQDNFormat, fqdnPrefix, location)
}
// GetMasterAllowedSizes returns the master allowed sizes
func GetMasterAllowedSizes() string{
return `` "allowedValues": [
"@
$first = $TRUE
ForEach ($k in ($MasterMap.Keys | Sort-Object)) {
if ($first -eq $TRUE)
{
$first = $FALSE
}
else
{
$text += ",`r`n"
}
$text += ' "' + $MasterMap.Item($k).Name + '"'
}
$text += @"
],
``
}
// GetAgentAllowedSizes returns the agent allowed sizes
func GetAgentAllowedSizes() string {
return `` "allowedValues": [
"@
$first = $TRUE
ForEach ($k in ($AgentMap.Keys | Sort-Object)) {
if ($first -eq $TRUE)
{
$first = $FALSE
}
else
{
$text += ",`r`n"
}
$text += ' "' + $AgentMap.Item($k).Name + '"'
}
$text += @"
],
``
}
// GetSizeMap returns the size / storage map
func GetSizeMap() string{
return `` "vmSizesMap": {
"@
# merge the maps
$mergedMap = @{}
ForEach ($k in $AgentMap.Keys) {
$size = $AgentMap.Item($k)
if (!$mergedMap.ContainsKey($k)) {
$mergedMap.Add($size.Name, $size)
}
}
ForEach ($k in $MasterMap.Keys) {
$size = $MasterMap.Item($k)
if (!$mergedMap.ContainsKey($k)) {
$mergedMap.Add($size.Name, $size)
}
}
$first = $TRUE
ForEach ($k in ($mergedMap.Keys | Sort-Object)) {
$size = $mergedMap.Item($k)
if ($first -eq $TRUE)
{
$first = $FALSE
}
else
{
$text += ",`r`n"
}
$text += ' "' + $size.Name + '": {' + "`r`n"
if ($size.Name.Contains("GS") -Or $size.Name.Contains("DS")) {
$text += ' "storageAccountType": "Premium_LRS"' + "`r`n"
}
else
{
$text += ' "storageAccountType": "Standard_LRS"' + "`r`n"
}
$text += ' }'
}
$text += @"
}
``
}
"@
return $text
}
try
{
$allSizes = Get-AllSizes
$masterMap = Get-MasterMap -SizeMap $allSizes
$agentMap = Get-AgentMap -SizeMap $allSizes
$locations = Get-Locations
$text = Get-FileContents -MasterMap $masterMap -AgentMap $agentMap -Locations $locations
$text | Out-File $OutFile
(Get-Content $OutFile) -replace "`0", "" | Set-Content $OutFile
}
catch
{
Write-Error $_
}

432
acstgen/tgen/azureconst.go Normal file
Просмотреть файл

@ -0,0 +1,432 @@
package tgen
import "fmt"
// AUTOGENERATED FILE - last generated 2016-10-05 06:09:49Z
const (
// AzureProdFQDNFormat specifies the format for a prod dns name
AzureProdFQDNFormat = "%s.%s.cloudapp.azure.com"
)
// AzureLocations provides all azure regions in prod.
// Related powershell to refresh this list:
// Get-AzureRmLocation | Select-Object -Property Location
var AzureLocations = []string{
"australiaeast",
"australiasoutheast",
"brazilsouth",
"canadacentral",
"canadaeast",
"centralindia",
"centralus",
"eastasia",
"eastus",
"eastus2",
"japaneast",
"japanwest",
"northcentralus",
"northeurope",
"southcentralus",
"southeastasia",
"southindia",
"uksouth",
"ukwest",
"westcentralus",
"westeurope",
"westindia",
"westus",
"westus2",
}
// FormatAzureProdFQDNs constructs all possible Azure prod fqdn
func FormatAzureProdFQDNs(fqdnPrefix string) []string {
var fqdns []string
for _, location := range AzureLocations {
fqdns = append(fqdns, FormatAzureProdFQDN(fqdnPrefix, location))
}
return fqdns
}
// FormatAzureProdFQDN constructs an Azure prod fqdn
func FormatAzureProdFQDN(fqdnPrefix string, location string) string {
return fmt.Sprintf(AzureProdFQDNFormat, fqdnPrefix, location)
}
// GetMasterAllowedSizes returns the master allowed sizes
func GetMasterAllowedSizes() string{
return ` "allowedValues": [
"Basic_A3",
"Basic_A4",
"Standard_A10",
"Standard_A11",
"Standard_A2",
"Standard_A3",
"Standard_A4",
"Standard_A5",
"Standard_A6",
"Standard_A7",
"Standard_A8",
"Standard_A9",
"Standard_D11",
"Standard_D11_v2",
"Standard_D12",
"Standard_D12_v2",
"Standard_D13",
"Standard_D13_v2",
"Standard_D14",
"Standard_D14_v2",
"Standard_D15_v2",
"Standard_D2",
"Standard_D2_v2",
"Standard_D3",
"Standard_D3_v2",
"Standard_D4",
"Standard_D4_v2",
"Standard_D5_v2",
"Standard_DS13",
"Standard_DS13_v2",
"Standard_DS14",
"Standard_DS14_v2",
"Standard_DS15_v2",
"Standard_DS5_v2",
"Standard_F16",
"Standard_F8",
"Standard_G1",
"Standard_G2",
"Standard_G3",
"Standard_G4",
"Standard_G5",
"Standard_GS2",
"Standard_GS3",
"Standard_GS4",
"Standard_GS5",
"Standard_H16",
"Standard_H16m",
"Standard_H16mr",
"Standard_H16r",
"Standard_H8",
"Standard_H8m",
"Standard_NC12",
"Standard_NC24",
"Standard_NC6",
"Standard_NV12",
"Standard_NV24",
"Standard_NV6"
],
`
}
// GetAgentAllowedSizes returns the agent allowed sizes
func GetAgentAllowedSizes() string {
return ` "allowedValues": [
"Basic_A2",
"Basic_A3",
"Basic_A4",
"Standard_A10",
"Standard_A11",
"Standard_A2",
"Standard_A3",
"Standard_A4",
"Standard_A5",
"Standard_A6",
"Standard_A7",
"Standard_A8",
"Standard_A9",
"Standard_D11",
"Standard_D11_v2",
"Standard_D12",
"Standard_D12_v2",
"Standard_D13",
"Standard_D13_v2",
"Standard_D14",
"Standard_D14_v2",
"Standard_D15_v2",
"Standard_D2",
"Standard_D2_v2",
"Standard_D3",
"Standard_D3_v2",
"Standard_D4",
"Standard_D4_v2",
"Standard_D5_v2",
"Standard_DS11",
"Standard_DS11_v2",
"Standard_DS12",
"Standard_DS12_v2",
"Standard_DS13",
"Standard_DS13_v2",
"Standard_DS14",
"Standard_DS14_v2",
"Standard_DS15_v2",
"Standard_DS2",
"Standard_DS2_v2",
"Standard_DS3",
"Standard_DS3_v2",
"Standard_DS4",
"Standard_DS4_v2",
"Standard_DS5_v2",
"Standard_F16",
"Standard_F16s",
"Standard_F2",
"Standard_F2s",
"Standard_F4",
"Standard_F4s",
"Standard_F8",
"Standard_F8s",
"Standard_G1",
"Standard_G2",
"Standard_G3",
"Standard_G4",
"Standard_G5",
"Standard_GS1",
"Standard_GS2",
"Standard_GS3",
"Standard_GS4",
"Standard_GS5",
"Standard_H16",
"Standard_H16m",
"Standard_H16mr",
"Standard_H16r",
"Standard_H8",
"Standard_H8m",
"Standard_NC12",
"Standard_NC24",
"Standard_NC6",
"Standard_NV12",
"Standard_NV24",
"Standard_NV6"
],
`
}
// GetSizeMap returns the size / storage map
func GetSizeMap() string{
return ` "vmSizesMap": {
"Basic_A2": {
"storageAccountType": "Standard_LRS"
},
"Basic_A3": {
"storageAccountType": "Standard_LRS"
},
"Basic_A4": {
"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_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_D15_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_DS11": {
"storageAccountType": "Premium_LRS"
},
"Standard_DS11_v2": {
"storageAccountType": "Premium_LRS"
},
"Standard_DS12": {
"storageAccountType": "Premium_LRS"
},
"Standard_DS12_v2": {
"storageAccountType": "Premium_LRS"
},
"Standard_DS13": {
"storageAccountType": "Premium_LRS"
},
"Standard_DS13_v2": {
"storageAccountType": "Premium_LRS"
},
"Standard_DS14": {
"storageAccountType": "Premium_LRS"
},
"Standard_DS14_v2": {
"storageAccountType": "Premium_LRS"
},
"Standard_DS15_v2": {
"storageAccountType": "Premium_LRS"
},
"Standard_DS2": {
"storageAccountType": "Premium_LRS"
},
"Standard_DS2_v2": {
"storageAccountType": "Premium_LRS"
},
"Standard_DS3": {
"storageAccountType": "Premium_LRS"
},
"Standard_DS3_v2": {
"storageAccountType": "Premium_LRS"
},
"Standard_DS4": {
"storageAccountType": "Premium_LRS"
},
"Standard_DS4_v2": {
"storageAccountType": "Premium_LRS"
},
"Standard_DS5_v2": {
"storageAccountType": "Premium_LRS"
},
"Standard_F16": {
"storageAccountType": "Standard_LRS"
},
"Standard_F16s": {
"storageAccountType": "Standard_LRS"
},
"Standard_F2": {
"storageAccountType": "Standard_LRS"
},
"Standard_F2s": {
"storageAccountType": "Standard_LRS"
},
"Standard_F4": {
"storageAccountType": "Standard_LRS"
},
"Standard_F4s": {
"storageAccountType": "Standard_LRS"
},
"Standard_F8": {
"storageAccountType": "Standard_LRS"
},
"Standard_F8s": {
"storageAccountType": "Standard_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"
},
"Standard_H16": {
"storageAccountType": "Standard_LRS"
},
"Standard_H16m": {
"storageAccountType": "Standard_LRS"
},
"Standard_H16mr": {
"storageAccountType": "Standard_LRS"
},
"Standard_H16r": {
"storageAccountType": "Standard_LRS"
},
"Standard_H8": {
"storageAccountType": "Standard_LRS"
},
"Standard_H8m": {
"storageAccountType": "Standard_LRS"
},
"Standard_NC12": {
"storageAccountType": "Standard_LRS"
},
"Standard_NC24": {
"storageAccountType": "Standard_LRS"
},
"Standard_NC6": {
"storageAccountType": "Standard_LRS"
},
"Standard_NV12": {
"storageAccountType": "Standard_LRS"
},
"Standard_NV24": {
"storageAccountType": "Standard_LRS"
},
"Standard_NV6": {
"storageAccountType": "Standard_LRS"
}
}
`
}

25
acstgen/tgen/const.go Normal file
Просмотреть файл

@ -0,0 +1,25 @@
package tgen
const (
// BaseLBPriority specifies the base lb priority.
BaseLBPriority = 200
// DefaultMasterSubnet specifies the default master subnet for DCOS or Swarm
DefaultMasterSubnet = "172.16.0.0/24"
// DefaultFirstConsecutiveStaticIP specifies the static IP address on master 0 for DCOS or Swarm
DefaultFirstConsecutiveStaticIP = "172.16.0.5"
// DefaultSwarmWindowsMasterSubnet specifies the default master subnet for a Swarm Windows cluster
DefaultSwarmWindowsMasterSubnet = "192.168.255.0/24"
// DefaultSwarmWindowsFirstConsecutiveStaticIP specifies the static IP address on master 0 for a Swarm WIndows cluster
DefaultSwarmWindowsFirstConsecutiveStaticIP = "192.168.255.5"
// DefaultKubernetesMasterSubnet specifies the default kubernetes master subnet
DefaultKubernetesMasterSubnet = "10.240.0.0/16"
// DefaultFirstConsecutiveKubernetesStaticIP specifies the static IP address on Kubernetes master 0
DefaultFirstConsecutiveKubernetesStaticIP = "10.240.255.5"
// DefaultAgentSubnetTemplate specifies a default agent subnet
DefaultAgentSubnetTemplate = "10.%d.0.0/24"
// DefaultKubernetesClusterDomain is the dns suffix used in the cluster (used as a SAN in the PKI generation)
DefaultKubernetesClusterDomain = "cluster.local"
// KubernetesHyperkubeSpec is the hyperkube version used for Kubernetes setup
// The latest stable version can be found here: https://storage.googleapis.com/kubernetes-release/release/stable.txt
KubernetesHyperkubeSpec = "gcr.io/google_containers/hyperkube-amd64:v1.4.0"
)

129
acstgen/tgen/defaults.go Normal file
Просмотреть файл

@ -0,0 +1,129 @@
package tgen
import (
"fmt"
"net"
"./../api/vlabs"
"./../util"
)
// SetAcsClusterDefaults for an AcsCluster, returns true if certs are generated
func SetAcsClusterDefaults(a *vlabs.AcsCluster) (bool, error) {
setMasterNetworkDefaults(a)
setAgentNetworkDefaults(a)
setStorageDefaults(a)
certsGenerated, e := setDefaultCerts(a)
if e != nil {
return false, e
}
return certsGenerated, nil
}
// SetMasterNetworkDefaults for masters
func setMasterNetworkDefaults(a *vlabs.AcsCluster) {
if !a.MasterProfile.IsCustomVNET() {
if a.OrchestratorProfile.OrchestratorType == vlabs.Kubernetes {
a.MasterProfile.SetSubnet(DefaultKubernetesMasterSubnet)
a.MasterProfile.FirstConsecutiveStaticIP = DefaultFirstConsecutiveKubernetesStaticIP
} else if a.HasWindows() {
a.MasterProfile.SetSubnet(DefaultSwarmWindowsMasterSubnet)
a.MasterProfile.FirstConsecutiveStaticIP = DefaultSwarmWindowsFirstConsecutiveStaticIP
} else {
a.MasterProfile.SetSubnet(DefaultMasterSubnet)
a.MasterProfile.FirstConsecutiveStaticIP = DefaultFirstConsecutiveStaticIP
}
}
}
// SetAgentNetworkDefaults for agents
func setAgentNetworkDefaults(a *vlabs.AcsCluster) {
// configure the subnets if not in custom VNET
if !a.MasterProfile.IsCustomVNET() {
subnetCounter := 0
for i := range a.AgentPoolProfiles {
profile := &a.AgentPoolProfiles[i]
if a.OrchestratorProfile.OrchestratorType == vlabs.Kubernetes {
profile.SetSubnet(a.MasterProfile.GetSubnet())
} else {
profile.SetSubnet(fmt.Sprintf(DefaultAgentSubnetTemplate, subnetCounter))
}
subnetCounter++
}
}
}
// setStorageDefaults for agents
func setStorageDefaults(a *vlabs.AcsCluster) {
for i := range a.AgentPoolProfiles {
profile := &a.AgentPoolProfiles[i]
if len(profile.StorageType) == 0 {
profile.StorageType = vlabs.StorageExternal
}
}
}
func setDefaultCerts(a *vlabs.AcsCluster) (bool, error) {
if !certGenerationRequired(a) {
return false, nil
}
masterWildCardFQDN := FormatAzureProdFQDN(a.MasterProfile.DNSPrefix, "*")
masterExtraFQDNs := FormatAzureProdFQDNs(a.MasterProfile.DNSPrefix)
firstMasterIP := net.ParseIP(a.MasterProfile.FirstConsecutiveStaticIP)
if firstMasterIP == nil {
return false, fmt.Errorf("MasterProfile.FirstConsecutiveStaticIP '%s' is an invalid IP address", a.MasterProfile.FirstConsecutiveStaticIP)
}
ips := []net.IP{firstMasterIP}
for i := 1; i < a.MasterProfile.Count; i++ {
ips = append(ips, net.IP{firstMasterIP[12], firstMasterIP[13], firstMasterIP[14], firstMasterIP[15] + byte(i)})
}
caPair, apiServerPair, clientPair, kubeConfigPair, err := util.CreatePki(masterWildCardFQDN, masterExtraFQDNs, ips, DefaultKubernetesClusterDomain)
if err != nil {
return false, err
}
a.CertificateProfile.APIServerCertificate = apiServerPair.CertificatePem
a.CertificateProfile.APIServerPrivateKey = apiServerPair.PrivateKeyPem
a.CertificateProfile.CaCertificate = caPair.CertificatePem
a.CertificateProfile.SetCAPrivateKey(caPair.PrivateKeyPem)
a.CertificateProfile.ClientCertificate = clientPair.CertificatePem
a.CertificateProfile.ClientPrivateKey = clientPair.PrivateKeyPem
a.CertificateProfile.KubeConfigCertificate = kubeConfigPair.CertificatePem
a.CertificateProfile.KubeConfigPrivateKey = kubeConfigPair.PrivateKeyPem
return true, nil
}
func certGenerationRequired(a *vlabs.AcsCluster) bool {
if len(a.CertificateProfile.APIServerCertificate) > 0 || len(a.CertificateProfile.APIServerPrivateKey) > 0 ||
len(a.CertificateProfile.CaCertificate) > 0 ||
len(a.CertificateProfile.ClientCertificate) > 0 || len(a.CertificateProfile.ClientPrivateKey) > 0 {
return false
}
switch a.OrchestratorProfile.OrchestratorType {
case vlabs.DCOS:
return false
case vlabs.DCOS184:
return false
case vlabs.DCOS173:
return false
case vlabs.Swarm:
return false
case vlabs.Kubernetes:
return true
default:
return false
}
}

2
acstgen/tgen/doc.go Normal file
Просмотреть файл

@ -0,0 +1,2 @@
// Package tgen takes an ACS cluster model and generates the corresponding template
package tgen

630
acstgen/tgen/template.go Normal file
Просмотреть файл

@ -0,0 +1,630 @@
package tgen
import (
"bytes"
"compress/gzip"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"hash/fnv"
"io/ioutil"
"math/rand"
"os"
"path"
"regexp"
"strings"
"text/template"
"./../api/vlabs"
)
const (
kubernetesMasterCustomDataYaml = "kubernetesmastercustomdata.yml"
kubernetesMasterCustomScript = "kubernetesmastercustomscript.sh"
kubernetesAgentCustomDataYaml = "kubernetesagentcustomdata.yml"
kubernetesAgentCustomScript = "kubernetesagentcustomscript.sh"
kubeConfigJSON = "kubeconfig.json"
)
const (
agentOutputs = "agentoutputs.t"
agentParams = "agentparams.t"
classicParams = "classicparams.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"
kubernetesBaseFile = "kubernetesbase.t"
kubernetesAgentResources = "kubernetesagentresources.t"
kubernetesAgentVars = "kubernetesagentvars.t"
kubernetesMasterResources = "kubernetesmasterresources.t"
kubernetesMasterVars = "kubernetesmastervars.t"
kubernetesParams = "kubernetesparams.t"
masterOutputs = "masteroutputs.t"
masterParams = "masterparams.t"
swarmBaseFile = "swarmbase.t"
swarmAgentCustomData = "swarmagentcustomdata.t"
swarmAgentResources = "swarmagentresources.t"
swarmAgentResourcesDisks = "swarmagentresourcesdisks.t"
swarmAgentVars = "swarmagentvars.t"
swarmMasterCustomData = "swarmmastercustomdata.t"
swarmMasterResources = "swarmmasterresources.t"
swarmMasterVars = "swarmmastervars.t"
swarmWinAgentResources = "swarmwinagentresources.t"
swarmWinAgentResourcesDisks = "swarmwinagentresourcesdisks.t"
windowsParams = "windowsparams.t"
)
var commonTemplateFiles = []string{agentOutputs, agentParams, classicParams, masterOutputs, masterParams}
var dcosTemplateFiles = []string{dcosAgentResources, dcosAgentResourcesDisks, dcosAgentVars, dcosBaseFile, dcosCustomData173, dcosCustomData184, dcosMasterResources, dcosMasterVars}
var kubernetesTemplateFiles = []string{kubernetesBaseFile, kubernetesAgentResources, kubernetesAgentVars, kubernetesMasterResources, kubernetesMasterVars, kubernetesParams}
var swarmTemplateFiles = []string{swarmBaseFile, swarmAgentCustomData, swarmAgentResources, swarmAgentVars, swarmAgentResourcesDisks, swarmBaseFile, swarmMasterCustomData, swarmMasterResources, swarmMasterVars, swarmWinAgentResources, swarmWinAgentResourcesDisks, windowsParams}
// VerifyFiles verifies that the required template files exist
func VerifyFiles(partsDirectory string) error {
allFiles := append(commonTemplateFiles, dcosTemplateFiles...)
allFiles = append(allFiles, kubernetesTemplateFiles...)
allFiles = append(allFiles, swarmTemplateFiles...)
for _, file := range allFiles {
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, string, error) {
var err error
var templ *template.Template
templ = template.New("acs template").Funcs(getTemplateFuncMap(acsCluster, partsDirectory))
var files []string
var baseFile string
if acsCluster.OrchestratorProfile.OrchestratorType == vlabs.DCOS184 ||
acsCluster.OrchestratorProfile.OrchestratorType == vlabs.DCOS ||
acsCluster.OrchestratorProfile.OrchestratorType == vlabs.DCOS173 {
files = append(commonTemplateFiles, dcosTemplateFiles...)
baseFile = dcosBaseFile
} else if acsCluster.OrchestratorProfile.OrchestratorType == vlabs.Swarm {
files = append(commonTemplateFiles, swarmTemplateFiles...)
baseFile = swarmBaseFile
} else if acsCluster.OrchestratorProfile.OrchestratorType == vlabs.Kubernetes {
files = append(commonTemplateFiles, kubernetesTemplateFiles...)
baseFile = kubernetesBaseFile
} else {
return "", "", fmt.Errorf("orchestrator '%s' is unsupported", acsCluster.OrchestratorProfile.OrchestratorType)
}
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
}
var parametersMap *map[string]interface{}
if parametersMap, err = getParameters(acsCluster); err != nil {
return "", "", err
}
var parameterBytes []byte
if parameterBytes, err = json.Marshal(parametersMap); err != nil {
return "", "", err
}
return b.String(), string(parameterBytes), nil
}
// GenerateClusterID creates a unique 8 string cluster ID
func GenerateClusterID(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]
}
// GenerateKubeConfig returns a JSON string representing the KubeConfig
func GenerateKubeConfig(acsCluster *vlabs.AcsCluster, templateDirectory string, location string) (string, error) {
kubeTemplateFile := path.Join(templateDirectory, kubeConfigJSON)
if _, err := os.Stat(kubeTemplateFile); os.IsNotExist(err) {
return "", fmt.Errorf("file %s does not exist, did you specify the correct template directory?", kubeTemplateFile)
}
b, err := ioutil.ReadFile(kubeTemplateFile)
if err != nil {
return "", fmt.Errorf("error reading kube config template file %s: %s", kubeTemplateFile, err.Error())
}
kubeconfig := string(b)
// variable replacement
kubeconfig = strings.Replace(kubeconfig, "<<<variables('caCertificate')>>>", base64.StdEncoding.EncodeToString([]byte(acsCluster.CertificateProfile.CaCertificate)), -1)
kubeconfig = strings.Replace(kubeconfig, "<<<reference(concat('Microsoft.Network/publicIPAddresses/', variables('masterPublicIPAddressName'))).dnsSettings.fqdn>>>", FormatAzureProdFQDN(acsCluster.MasterProfile.DNSPrefix, location), -1)
kubeconfig = strings.Replace(kubeconfig, "{{{resourceGroup}}}", acsCluster.MasterProfile.DNSPrefix, -1)
kubeconfig = strings.Replace(kubeconfig, "<<<variables('kubeConfigCertificate')>>>", base64.StdEncoding.EncodeToString([]byte(acsCluster.CertificateProfile.KubeConfigCertificate)), -1)
kubeconfig = strings.Replace(kubeconfig, "<<<variables('kubeConfigPrivateKey')>>>", base64.StdEncoding.EncodeToString([]byte(acsCluster.CertificateProfile.KubeConfigPrivateKey)), -1)
return kubeconfig, nil
}
func getParameters(acsCluster *vlabs.AcsCluster) (*map[string]interface{}, error) {
parametersMap := &map[string]interface{}{}
// Master Parameters
addValue(parametersMap, "linuxAdminUsername", acsCluster.LinuxProfile.AdminUsername)
addValue(parametersMap, "masterEndpointDNSNamePrefix", acsCluster.MasterProfile.DNSPrefix)
if acsCluster.MasterProfile.IsCustomVNET() {
addValue(parametersMap, "masterVnetSubnetID", acsCluster.MasterProfile.VnetSubnetID)
} else {
addValue(parametersMap, "masterSubnet", acsCluster.MasterProfile.GetSubnet())
}
addValue(parametersMap, "firstConsecutiveStaticIP", acsCluster.MasterProfile.FirstConsecutiveStaticIP)
addValue(parametersMap, "masterVMSize", acsCluster.MasterProfile.VMSize)
addValue(parametersMap, "sshRSAPublicKey", acsCluster.LinuxProfile.SSH.PublicKeys[0].KeyData)
// Kubernetes Parameters
if acsCluster.OrchestratorProfile.OrchestratorType == vlabs.Kubernetes {
addValue(parametersMap, "apiServerCertificate", base64.StdEncoding.EncodeToString([]byte(acsCluster.CertificateProfile.APIServerCertificate)))
addValue(parametersMap, "apiServerPrivateKey", base64.StdEncoding.EncodeToString([]byte(acsCluster.CertificateProfile.APIServerPrivateKey)))
addValue(parametersMap, "caCertificate", base64.StdEncoding.EncodeToString([]byte(acsCluster.CertificateProfile.CaCertificate)))
addValue(parametersMap, "clientCertificate", base64.StdEncoding.EncodeToString([]byte(acsCluster.CertificateProfile.ClientCertificate)))
addValue(parametersMap, "clientPrivateKey", base64.StdEncoding.EncodeToString([]byte(acsCluster.CertificateProfile.ClientPrivateKey)))
addValue(parametersMap, "kubeConfigCertificate", base64.StdEncoding.EncodeToString([]byte(acsCluster.CertificateProfile.KubeConfigCertificate)))
addValue(parametersMap, "kubeConfigPrivateKey", base64.StdEncoding.EncodeToString([]byte(acsCluster.CertificateProfile.KubeConfigPrivateKey)))
addValue(parametersMap, "kubernetesHyperkubeSpec", KubernetesHyperkubeSpec)
addValue(parametersMap, "servicePrincipalClientId", acsCluster.ServicePrincipalProfile.ClientID)
addValue(parametersMap, "servicePrincipalClientSecret", acsCluster.ServicePrincipalProfile.Secret)
}
// Agent parameters
for _, agentProfile := range acsCluster.AgentPoolProfiles {
addValue(parametersMap, fmt.Sprintf("%sCount", agentProfile.Name), agentProfile.Count)
addValue(parametersMap, fmt.Sprintf("%sVMSize", agentProfile.Name), agentProfile.VMSize)
if agentProfile.IsCustomVNET() {
addValue(parametersMap, fmt.Sprintf("%sVnetSubnetID", agentProfile.Name), agentProfile.VnetSubnetID)
} else {
addValue(parametersMap, fmt.Sprintf("%sSubnet", agentProfile.Name), agentProfile.GetSubnet())
}
if len(agentProfile.Ports) > 0 {
addValue(parametersMap, fmt.Sprintf("%sEndpointDNSNamePrefix", agentProfile.Name), agentProfile.DNSPrefix)
}
}
// Windows parameters
if acsCluster.HasWindows() {
addValue(parametersMap, "windowsAdminUsername", acsCluster.WindowsProfile.AdminUsername)
addValue(parametersMap, "windowsAdminPassword", acsCluster.WindowsProfile.AdminPassword)
}
return parametersMap, nil
}
func addValue(m *map[string]interface{}, k string, v interface{}) {
(*m)[k] = *(&map[string]interface{}{})
(*m)[k].(map[string]interface{})["value"] = v
}
// getTemplateFuncMap returns all functions used in template generation
func getTemplateFuncMap(acsCluster *vlabs.AcsCluster, partsDirectory string) map[string]interface{} {
return 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
},
"RequiresFakeAgentOutput": func() bool {
return acsCluster.OrchestratorProfile.OrchestratorType == vlabs.Kubernetes
},
"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)
},
"GetUniqueNameSuffix": func() string {
return GenerateClusterID(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)
},
"GetMasterAllowedSizes": func() string {
return GetMasterAllowedSizes()
},
"GetAgentAllowedSizes": func() string {
return GetAgentAllowedSizes()
},
"GetSizeMap": func() string {
return GetSizeMap()
},
"Base64": func(s string) string {
return base64.StdEncoding.EncodeToString([]byte(s))
},
"GetKubernetesMasterCustomScript": func() string {
return getBase64CustomScript(acsCluster, kubernetesMasterCustomScript, partsDirectory)
},
"GetKubernetesMasterCustomData": func() string {
str, e := getSingleLineForTemplate(kubernetesMasterCustomDataYaml, partsDirectory)
if e != nil {
return ""
}
// add the master provisioning script
masterProvisionB64GzipStr := getBase64CustomScript(acsCluster, kubernetesMasterCustomScript, partsDirectory)
str = strings.Replace(str, "MASTER_PROVISION_B64_GZIP_STR", masterProvisionB64GzipStr, -1)
// return the custom data
return fmt.Sprintf("\"customData\": \"[base64(concat('%s'))]\",", str)
},
"GetKubernetesAgentCustomData": func(profile *vlabs.AgentPoolProfile) string {
str, e := getSingleLineForTemplate(kubernetesAgentCustomDataYaml, partsDirectory)
if e != nil {
return ""
}
// add the agent provisioning script
agentProvisionB64GzipStr := getBase64CustomScript(acsCluster, kubernetesAgentCustomScript, partsDirectory)
str = strings.Replace(str, "AGENT_PROVISION_B64_GZIP_STR", agentProvisionB64GzipStr, -1)
return fmt.Sprintf("\"customData\": \"[base64(concat('%s'))]\",", str)
},
"GetKubernetesKubeConfig": func() string {
str, e := getSingleLineForTemplate(kubeConfigJSON, partsDirectory)
if e != nil {
return ""
}
return str
},
"GetMasterSecrets": func() string {
clientPrivateKey := base64.StdEncoding.EncodeToString([]byte(acsCluster.CertificateProfile.ClientPrivateKey))
serverPrivateKey := base64.StdEncoding.EncodeToString([]byte(acsCluster.CertificateProfile.APIServerPrivateKey))
return fmt.Sprintf("%s %s %s %s", acsCluster.ServicePrincipalProfile.ClientID, acsCluster.ServicePrincipalProfile.Secret, clientPrivateKey, serverPrivateKey)
},
"GetAgentSecrets": func() string {
clientPrivateKey := base64.StdEncoding.EncodeToString([]byte(acsCluster.CertificateProfile.ClientPrivateKey))
return fmt.Sprintf("%s %s %s", acsCluster.ServicePrincipalProfile.ClientID, acsCluster.ServicePrincipalProfile.Secret, clientPrivateKey)
},
"AnyAgentHasDisks": func() bool {
for _, agentProfile := range acsCluster.AgentPoolProfiles {
if agentProfile.HasDisks() {
return true
}
}
return false
},
// 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
},
}
}
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 {
visitedSubnets := make(map[string]bool)
var buf bytes.Buffer
buf.WriteString(`"[variables('masterSubnet')]"`)
visitedSubnets[acsCluster.MasterProfile.GetSubnet()] = true
for i := range acsCluster.AgentPoolProfiles {
profile := &acsCluster.AgentPoolProfiles[i]
if _, ok := visitedSubnets[profile.GetSubnet()]; !ok {
buf.WriteString(fmt.Sprintf(",\n \"[variables('%sSubnet')]\"", profile.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, 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\"},`
}
// private agents
return `{\"content\": \"\", \"path\": \"/etc/mesosphere/roles/slave\"},`
}
// getSingleLineForTemplate returns the file as a single line for embedding in an arm template
func getSingleLineForTemplate(yamlFilename string, partsDirectory string) (string, error) {
yamlFile := path.Join(partsDirectory, yamlFilename)
if _, err := os.Stat(yamlFile); os.IsNotExist(err) {
return "", fmt.Errorf("yaml file %s does not exist, did you specify the correct template directory?", yamlFile)
}
b, err := ioutil.ReadFile(yamlFile)
if err != nil {
return "", fmt.Errorf("error reading yaml file %s: %s", yamlFile, err.Error())
}
// template.JSEscapeString leaves undesirable chars that don't work with pretty print
yamlStr := string(b)
yamlStr = strings.Replace(yamlStr, "\\", "\\\\", -1)
yamlStr = strings.Replace(yamlStr, "\r\n", "\\n", -1)
yamlStr = strings.Replace(yamlStr, "\n", "\\n", -1)
yamlStr = strings.Replace(yamlStr, "\"", "\\\"", -1)
// variable replacement
rVariable, e1 := regexp.Compile("{{{([^}]*)}}}")
if e1 != nil {
return "", e1
}
yamlStr = rVariable.ReplaceAllString(yamlStr, "',variables('$1'),'")
// verbatim replacement
rVerbatim, e2 := regexp.Compile("<<<([^>]*)>>>")
if e2 != nil {
return "", e2
}
yamlStr = rVerbatim.ReplaceAllString(yamlStr, "',$1,'")
return yamlStr, nil
}
// getBase64CustomScript will return a base64 of the CSE
func getBase64CustomScript(a *vlabs.AcsCluster, csFilename string, partsDirectory string) string {
csFile := path.Join(partsDirectory, csFilename)
if _, err := os.Stat(csFile); os.IsNotExist(err) {
panic(err.Error())
}
b, err := ioutil.ReadFile(csFile)
if err != nil {
panic(err.Error())
}
// translate the parameters
csStr := string(b)
csStr = strings.Replace(csStr, "\r\n", "\n", -1)
var gzipB bytes.Buffer
w := gzip.NewWriter(&gzipB)
w.Write([]byte(csStr))
w.Close()
return base64.StdEncoding.EncodeToString(gzipB.Bytes())
}

2
acstgen/util/doc.go Normal file
Просмотреть файл

@ -0,0 +1,2 @@
// Package util provides various utility functions for the generator
package util

171
acstgen/util/pki.go Normal file
Просмотреть файл

@ -0,0 +1,171 @@
package util
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"net"
"os"
"time"
)
const (
ValidityDuration = time.Hour * 24 * 365 * 2
PkiKeySize = 4096
)
type PkiKeyCertPair struct {
CertificatePem string
PrivateKeyPem string
}
func CreatePki(masterFQDN string, extraFQDNs []string, extraIPs []net.IP, clusterDomain string) (*PkiKeyCertPair, *PkiKeyCertPair, *PkiKeyCertPair, *PkiKeyCertPair, error) {
extraFQDNs = append(extraFQDNs, fmt.Sprintf("kubernetes"))
extraFQDNs = append(extraFQDNs, fmt.Sprintf("kubernetes.default"))
extraFQDNs = append(extraFQDNs, fmt.Sprintf("kubernetes.default.svc"))
extraFQDNs = append(extraFQDNs, fmt.Sprintf("kubernetes.default.svc.%s", clusterDomain))
extraFQDNs = append(extraFQDNs, fmt.Sprintf("kubernetes.kube-system"))
extraFQDNs = append(extraFQDNs, fmt.Sprintf("kubernetes.kube-system.svc"))
extraFQDNs = append(extraFQDNs, fmt.Sprintf("kubernetes.kube-system.svc.%s", clusterDomain))
start := time.Now()
caCertificate, caPrivateKey, err := createCertificate("ca", nil, nil, false, "", nil, nil)
if err != nil {
return nil, nil, nil, nil, err
}
var (
apiServerCertificate *x509.Certificate
apiServerPrivateKey *rsa.PrivateKey
clientCertificate *x509.Certificate
clientPrivateKey *rsa.PrivateKey
kubeConfigCertificate *x509.Certificate
kubeConfigPrivateKey *rsa.PrivateKey
)
errors := make(chan error)
go func() {
var err error
apiServerCertificate, apiServerPrivateKey, err = createCertificate("apiserver", caCertificate, caPrivateKey, true, masterFQDN, extraFQDNs, extraIPs)
errors <- err
}()
go func() {
var err error
clientCertificate, clientPrivateKey, err = createCertificate("client", caCertificate, caPrivateKey, false, "", nil, nil)
errors <- err
}()
go func() {
var err error
kubeConfigCertificate, kubeConfigPrivateKey, err = createCertificate("client", caCertificate, caPrivateKey, false, "", nil, nil)
errors <- err
}()
e1 := <-errors
e2 := <-errors
e3 := <-errors
if e1 != nil {
return nil, nil, nil, nil, e1
}
if e2 != nil {
return nil, nil, nil, nil, e2
}
if e3 != nil {
return nil, nil, nil, nil, e2
}
fmt.Fprintf(os.Stderr, "cert creation took %s\n", time.Since(start))
return &PkiKeyCertPair{CertificatePem: string(certificateToPem(caCertificate.Raw)), PrivateKeyPem: string(privateKeyToPem(caPrivateKey))},
&PkiKeyCertPair{CertificatePem: string(certificateToPem(apiServerCertificate.Raw)), PrivateKeyPem: string(privateKeyToPem(apiServerPrivateKey))},
&PkiKeyCertPair{CertificatePem: string(certificateToPem(clientCertificate.Raw)), PrivateKeyPem: string(privateKeyToPem(clientPrivateKey))},
&PkiKeyCertPair{CertificatePem: string(certificateToPem(kubeConfigCertificate.Raw)), PrivateKeyPem: string(privateKeyToPem(kubeConfigPrivateKey))},
nil
}
func createCertificate(commonName string, caCertificate *x509.Certificate, caPrivateKey *rsa.PrivateKey, isServer bool, FQDN string, extraFQDNs []string, extraIPs []net.IP) (*x509.Certificate, *rsa.PrivateKey, error) {
var err error
isCA := (caCertificate == nil)
now := time.Now()
template := x509.Certificate{
Subject: pkix.Name{CommonName: commonName},
NotBefore: now,
NotAfter: now.Add(ValidityDuration),
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
BasicConstraintsValid: true,
}
if isCA {
template.KeyUsage |= x509.KeyUsageCertSign
template.IsCA = isCA
} else if isServer {
extraFQDNs = append(extraFQDNs, FQDN)
extraIPs = append(extraIPs, net.ParseIP("10.0.0.1"))
template.DNSNames = extraFQDNs
template.IPAddresses = extraIPs
template.ExtKeyUsage = append(template.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
} else {
template.ExtKeyUsage = append(template.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
}
snMax := new(big.Int).Lsh(big.NewInt(1), 128)
template.SerialNumber, err = rand.Int(rand.Reader, snMax)
if err != nil {
return nil, nil, err
}
privateKey, err := rsa.GenerateKey(rand.Reader, PkiKeySize)
var privateKeyToUse *rsa.PrivateKey
var certificateToUse *x509.Certificate
if !isCA {
privateKeyToUse = caPrivateKey
certificateToUse = caCertificate
} else {
privateKeyToUse = privateKey
certificateToUse = &template
}
certDerBytes, err := x509.CreateCertificate(rand.Reader, &template, certificateToUse, &privateKey.PublicKey, privateKeyToUse)
if err != nil {
return nil, nil, err
}
certificate, err := x509.ParseCertificate(certDerBytes)
if err != nil {
return nil, nil, err
}
return certificate, privateKey, nil
}
func certificateToPem(derBytes []byte) []byte {
pemBlock := &pem.Block{
Type: "CERTIFICATE",
Bytes: derBytes,
}
pemBuffer := bytes.Buffer{}
pem.Encode(&pemBuffer, pemBlock)
return pemBuffer.Bytes()
}
func privateKeyToPem(privateKey *rsa.PrivateKey) []byte {
pemBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
}
pemBuffer := bytes.Buffer{}
pem.Encode(&pemBuffer, pemBlock)
return pemBuffer.Bytes()
}