func NewManifest(config Config, iaasConfig iaas.Config) (Manifest, error) {
	config = NewConfigWithDefaults(config)

	releases := []core.Release{
		{
			Name:    "etcd",
			Version: "latest",
		},
	}

	cidr, err := core.ParseCIDRBlock(config.IPRange)
	if err != nil {
		return Manifest{}, err
	}

	etcdNetwork1 := core.Network{
		Name: "etcd1",
		Subnets: []core.NetworkSubnet{{
			CloudProperties: iaasConfig.NetworkSubnet(cidr.String()),
			Gateway:         cidr.GetFirstIP().Add(1).String(),
			Range:           cidr.String(),
			Reserved:        []string{cidr.Range(2, 3), cidr.GetLastIP().String()},
			Static:          []string{cidr.Range(4, cidr.CIDRSize-5)},
		}},
		Type: "manual",
	}

	compilation := core.Compilation{
		Network:             etcdNetwork1.Name,
		ReuseCompilationVMs: true,
		Workers:             3,
		CloudProperties:     iaasConfig.Compilation("us-east-1a"),
	}

	update := core.Update{
		Canaries:        1,
		CanaryWatchTime: "1000-180000",
		MaxInFlight:     1,
		Serial:          true,
		UpdateWatchTime: "1000-180000",
	}

	stemcell := core.ResourcePoolStemcell{
		Name:    iaasConfig.Stemcell(),
		Version: "latest",
	}

	z1ResourcePool := core.ResourcePool{
		Name:            "etcd_z1",
		Network:         etcdNetwork1.Name,
		Stemcell:        stemcell,
		CloudProperties: iaasConfig.ResourcePool(etcdNetwork1.Subnets[0].Range),
	}

	staticIPs, err := etcdNetwork1.StaticIPsFromRange(24)
	if err != nil {
		return Manifest{}, err
	}

	etcdZ1JobTemplates := []core.JobTemplate{
		{
			Name:    "etcd",
			Release: "etcd",
		},
	}

	etcdZ1Job := core.Job{
		Name:      "etcd_z1",
		Instances: 1,
		Networks: []core.JobNetwork{{
			Name:      etcdNetwork1.Name,
			StaticIPs: []string{staticIPs[0]},
		}},
		PersistentDisk: 1024,
		ResourcePool:   z1ResourcePool.Name,
		Templates:      etcdZ1JobTemplates,
	}

	if config.IPTablesAgent {
		etcdZ1Job.Templates = append(etcdZ1Job.Templates, core.JobTemplate{
			Name:    "iptables_agent",
			Release: "etcd",
		})
	}

	testconsumerZ1Job := core.Job{
		Name:      "testconsumer_z1",
		Instances: 1,
		Networks: []core.JobNetwork{{
			Name:      etcdNetwork1.Name,
			StaticIPs: []string{staticIPs[8]},
		}},
		PersistentDisk: 1024,
		ResourcePool:   z1ResourcePool.Name,
		Templates: []core.JobTemplate{
			{
				Name:    "etcd_testconsumer",
				Release: "etcd",
			},
		},
	}

	globalProperties := Properties{
		Etcd: &PropertiesEtcd{
			Cluster: []PropertiesEtcdCluster{{
				Instances: 1,
				Name:      "etcd_z1",
			}},
			Machines:                        etcdZ1Job.Networks[0].StaticIPs,
			PeerRequireSSL:                  false,
			RequireSSL:                      false,
			HeartbeatIntervalInMilliseconds: 50,
		},
		EtcdTestConsumer: &PropertiesEtcdTestConsumer{
			Etcd: PropertiesEtcdTestConsumerEtcd{
				Machines: etcdZ1Job.Networks[0].StaticIPs,
			},
		},
	}

	if config.TurbulenceHost != "" {
		globalProperties.TurbulenceAgent = &core.PropertiesTurbulenceAgent{
			API: core.PropertiesTurbulenceAgentAPI{
				Host:     config.TurbulenceHost,
				Password: turbulence.DefaultPassword,
				CACert:   turbulence.APICACert,
			},
		}

		etcdZ1Job.Templates = append(etcdZ1Job.Templates, core.JobTemplate{
			Name:    "turbulence_agent",
			Release: "turbulence",
		})

		releases = append(releases, core.Release{
			Name:    "turbulence",
			Version: "latest",
		})
	}

	return Manifest{
		DirectorUUID: config.DirectorUUID,
		Name:         config.Name,
		Compilation:  compilation,
		Jobs: []core.Job{
			etcdZ1Job,
			testconsumerZ1Job,
		},
		Networks: []core.Network{
			etcdNetwork1,
		},
		Properties: globalProperties,
		Releases:   releases,
		ResourcePools: []core.ResourcePool{
			z1ResourcePool,
		},
		Update: update,
	}, nil
}
func NewTLSUpgradeManifest(config Config, iaasConfig iaas.Config) Manifest {
	config = NewConfigWithDefaults(config)

	releases := []core.Release{
		{
			Name:    "etcd",
			Version: "latest",
		},
		{
			Name:    "consul",
			Version: "latest",
		},
	}

	compilation := core.Compilation{
		Network:             "etcd1",
		ReuseCompilationVMs: true,
		Workers:             3,
		CloudProperties:     iaasConfig.Compilation("us-east-1a"),
	}

	update := core.Update{
		Canaries:        1,
		CanaryWatchTime: "1000-180000",
		MaxInFlight:     1,
		Serial:          true,
		UpdateWatchTime: "1000-180000",
	}

	stemcell := core.ResourcePoolStemcell{
		Name:    iaasConfig.Stemcell(),
		Version: "latest",
	}

	resourcePools := []core.ResourcePool{
		{
			Name:            "etcd_z1",
			Network:         "etcd1",
			CloudProperties: iaasConfig.ResourcePool(config.IPRange),
			Stemcell:        stemcell,
		},
	}

	cidr, err := core.ParseCIDRBlock(config.IPRange)
	if err != nil {
		return Manifest{}
	}

	networks := []core.Network{
		{
			Name: "etcd1",
			Subnets: []core.NetworkSubnet{{
				CloudProperties: iaasConfig.NetworkSubnet(cidr.String()),
				Gateway:         cidr.GetFirstIP().Add(1).String(),
				Range:           cidr.String(),
				Reserved:        []string{cidr.Range(2, 3), cidr.GetLastIP().String()},
				Static:          []string{cidr.Range(4, cidr.CIDRSize-5)},
			}},
			Type: "manual",
		},
	}

	staticIPs, err := networks[0].StaticIPsFromRange(24)
	if err != nil {
		return Manifest{}
	}

	consulJob := core.Job{
		Name:      "consul_z1",
		Instances: 3,
		Networks: []core.JobNetwork{{
			Name: "etcd1",
			StaticIPs: []string{
				staticIPs[5],
				staticIPs[6],
				staticIPs[7],
			},
		}},
		PersistentDisk: 1024,
		ResourcePool:   "etcd_z1",
		Templates: []core.JobTemplate{
			{
				Name:    "consul_agent",
				Release: "consul",
			},
		},
		Properties: &core.JobProperties{
			Consul: &core.JobPropertiesConsul{
				Agent: core.JobPropertiesConsulAgent{
					Mode: "server",
				},
			},
		},
	}

	etcdTLS := core.Job{
		Name:      "etcd_tls_z1",
		Instances: 3,
		Networks: []core.JobNetwork{{
			Name: "etcd1",
			StaticIPs: []string{
				staticIPs[13],
				staticIPs[14],
				staticIPs[15],
			},
		}},
		PersistentDisk: 1024,
		ResourcePool:   "etcd_z1",
		Templates: []core.JobTemplate{
			{
				Name:    "consul_agent",
				Release: "consul",
			},
			{
				Name:    "etcd",
				Release: "etcd",
			},
		},
		Properties: &core.JobProperties{
			Consul: &core.JobPropertiesConsul{
				Agent: core.JobPropertiesConsulAgent{
					Services: core.JobPropertiesConsulAgentServices{
						"etcd": core.JobPropertiesConsulAgentService{},
					},
				},
			},
			Etcd: &core.JobPropertiesEtcd{
				Cluster: []core.JobPropertiesEtcdCluster{{
					Instances: 3,
					Name:      "etcd_tls_z1",
				}},
				Machines: []string{
					"etcd.service.cf.internal",
				},
				PeerRequireSSL:                  true,
				RequireSSL:                      true,
				HeartbeatIntervalInMilliseconds: 50,
				AdvertiseURLsDNSSuffix:          "etcd.service.cf.internal",
				CACert:                          config.Secrets.Etcd.CACert,
				ClientCert:                      config.Secrets.Etcd.ClientCert,
				ClientKey:                       config.Secrets.Etcd.ClientKey,
				PeerCACert:                      config.Secrets.Etcd.PeerCACert,
				PeerCert:                        config.Secrets.Etcd.PeerCert,
				PeerKey:                         config.Secrets.Etcd.PeerKey,
				ServerCert:                      config.Secrets.Etcd.ServerCert,
				ServerKey:                       config.Secrets.Etcd.ServerKey,
			},
		},
	}

	etcdNoTLS := core.Job{
		Name:      "etcd_z1",
		Instances: 1,
		Networks: []core.JobNetwork{{
			Name: "etcd1",
			StaticIPs: []string{
				staticIPs[0],
			},
		}},
		PersistentDisk: 1024,
		ResourcePool:   "etcd_z1",
		Templates: []core.JobTemplate{
			{
				Name:    "consul_agent",
				Release: "consul",
			},
			{
				Name:    "etcd_proxy",
				Release: "etcd",
			},
		},
	}

	testconsumerJob := core.Job{
		Name:      "testconsumer_z1",
		Instances: 5,
		Networks: []core.JobNetwork{{
			Name: "etcd1",
			StaticIPs: []string{
				staticIPs[8],
				staticIPs[9],
				staticIPs[10],
				staticIPs[11],
				staticIPs[12],
			},
		}},
		PersistentDisk: 1024,
		ResourcePool:   "etcd_z1",
		Templates: []core.JobTemplate{
			{
				Name:    "consul_agent",
				Release: "consul",
			},
			{
				Name:    "etcd_testconsumer",
				Release: "etcd",
			},
		},
		Properties: &core.JobProperties{
			EtcdTestConsumer: &core.JobPropertiesEtcdTestConsumer{
				Etcd: core.JobPropertiesEtcdTestConsumerEtcd{
					Machines: []string{
						"etcd.service.cf.internal",
					},
					RequireSSL: true,
					CACert:     config.Secrets.Etcd.CACert,
					ClientCert: config.Secrets.Etcd.ClientCert,
					ClientKey:  config.Secrets.Etcd.ClientKey,
				},
			},
		},
	}

	properties := Properties{
		Consul: &consul.PropertiesConsul{
			Agent: consul.PropertiesConsulAgent{
				Domain: "cf.internal",
				Servers: consul.PropertiesConsulAgentServers{
					Lan: []string{
						staticIPs[5],
						staticIPs[6],
						staticIPs[7],
					},
				},
			},
			CACert:      config.Secrets.Consul.CACert,
			AgentCert:   config.Secrets.Consul.AgentCert,
			AgentKey:    config.Secrets.Consul.AgentKey,
			ServerCert:  config.Secrets.Consul.ServerCert,
			ServerKey:   config.Secrets.Consul.ServerKey,
			EncryptKeys: []string{config.Secrets.Consul.EncryptKey},
		},
		EtcdProxy: &PropertiesEtcdProxy{
			Port: 4001,
			Etcd: PropertiesEtcdProxyEtcd{
				DNSSuffix:  "etcd.service.cf.internal",
				Port:       4001,
				CACert:     config.Secrets.Etcd.CACert,
				ClientCert: config.Secrets.Etcd.ClientCert,
				ClientKey:  config.Secrets.Etcd.ClientKey,
			},
		},
	}

	return Manifest{
		DirectorUUID:  config.DirectorUUID,
		Name:          config.Name,
		Releases:      releases,
		Compilation:   compilation,
		Update:        update,
		ResourcePools: resourcePools,
		Networks:      networks,
		Jobs: []core.Job{
			consulJob,
			etcdTLS,
			etcdNoTLS,
			testconsumerJob,
		},
		Properties: properties,
	}
}
func NewManifest(config Config, iaasConfig iaas.Config) (Manifest, error) {
	config.PopulateDefaultConfigNodes()

	cidrBlocks, err := config.GetCIDRBlocks()
	if err != nil {
		return Manifest{}, err
	}

	consulNetworks := []core.Network{}
	for i, cidrBlock := range cidrBlocks {
		consulNetwork := core.Network{
			Name: fmt.Sprintf("consul%d", i+1),
			Subnets: []core.NetworkSubnet{{
				CloudProperties: iaasConfig.NetworkSubnet(cidrBlock.String()),
				Gateway:         cidrBlock.GetFirstIP().Add(1).String(),
				Range:           cidrBlock.String(),
				Reserved:        []string{cidrBlock.Range(2, 3), cidrBlock.GetLastIP().String()},
				Static:          []string{cidrBlock.Range(4, cidrBlock.CIDRSize-5)},
			}},
			Type: "manual",
		}
		consulNetworks = append(consulNetworks, consulNetwork)
	}

	compilation := core.Compilation{
		Network:             consulNetworks[0].Name,
		ReuseCompilationVMs: true,
		Workers:             3,
		CloudProperties:     iaasConfig.Compilation("us-west-2a"),
	}

	stemcell := core.ResourcePoolStemcell{
		Name:    iaasConfig.Stemcell(),
		Version: "latest",
	}

	resourcePools := []core.ResourcePool{}
	for i, network := range consulNetworks {
		resourcePool := core.ResourcePool{
			Name:            fmt.Sprintf("consul_z%d", i+1),
			Network:         network.Name,
			Stemcell:        stemcell,
			CloudProperties: iaasConfig.ResourcePool(network.Subnets[0].Range),
		}
		resourcePools = append(resourcePools, resourcePool)
	}

	jobs := []core.Job{}
	consulClusterStaticIPs := []string{}

	for i := range consulNetworks {
		instances := config.Networks[i].Nodes

		staticIps, err := consulNetworks[i].StaticIPsFromRange(instances)
		if err != nil {
			return Manifest{}, err
		}

		job := core.Job{
			Name:      fmt.Sprintf("consul_z%d", i+1),
			Instances: instances,
			Networks: []core.JobNetwork{{
				Name:      consulNetworks[i].Name,
				StaticIPs: staticIps,
			}},
			PersistentDisk: 1024,
			Properties: &core.JobProperties{
				Consul: &core.JobPropertiesConsul{
					Agent: core.JobPropertiesConsulAgent{
						Mode:     "server",
						LogLevel: "info",
						Services: core.JobPropertiesConsulAgentServices{
							"router": core.JobPropertiesConsulAgentService{
								Name: "gorouter",
								Check: &core.JobPropertiesConsulAgentServiceCheck{
									Name:     "router-check",
									Script:   "/var/vcap/jobs/router/bin/script",
									Interval: "1m",
								},
								Tags: []string{"routing"},
							},
							"cloud_controller": core.JobPropertiesConsulAgentService{},
						},
					},
				},
			},
			ResourcePool: resourcePools[i].Name,
			Templates: []core.JobTemplate{{
				Name:    "consul_agent",
				Release: "consul",
			}},
			Update: &core.JobUpdate{
				MaxInFlight: 1,
			},
		}

		jobs = append(jobs, job)
		consulClusterStaticIPs = append(consulClusterStaticIPs, staticIps...)
	}

	staticIps, err := consulNetworks[0].StaticIPsFromRange(9)
	if err != nil {
		return Manifest{}, err
	}

	jobs = append(jobs, core.Job{
		Name:      "consul_test_consumer",
		Instances: 1,
		Networks: []core.JobNetwork{{
			Name: consulNetworks[0].Name,
			StaticIPs: []string{
				staticIps[6],
			},
		}},
		Properties: &core.JobProperties{
			Consul: &core.JobPropertiesConsul{
				Agent: core.JobPropertiesConsulAgent{
					Mode:     "client",
					LogLevel: "info",
				},
			},
		},
		ResourcePool: resourcePools[0].Name,
		Templates: []core.JobTemplate{
			{
				Name:    "consul_agent",
				Release: "consul",
			},
			{
				Name:    "consul-test-consumer",
				Release: "consul",
			},
		},
	})

	properties := Properties{
		Consul: &PropertiesConsul{
			Agent: PropertiesConsulAgent{
				Domain: "cf.internal",
				Servers: PropertiesConsulAgentServers{
					Lan: consulClusterStaticIPs,
				},
				DNSConfig: PropertiesConsulAgentDNSConfig{
					RecursorTimeout: "5s",
				},
			},
			EncryptKeys: []string{EncryptKey},
		},
	}

	overrideTLS(properties.Consul, config.DC)

	return Manifest{
		DirectorUUID:  config.DirectorUUID,
		Name:          config.Name,
		Releases:      releases(),
		Update:        update(),
		Compilation:   compilation,
		ResourcePools: resourcePools,
		Jobs:          jobs,
		Networks:      consulNetworks,
		Properties:    properties,
	}, nil
}