<commit_before>package acsengine

import (
	"fmt"
	"net"

	"github.com/Azure/acs-engine/pkg/api"
	"github.com/Masterminds/semver"
)

var (
	//AzureCloudSpec is the default configurations for global azure.
	AzureCloudSpec = AzureEnvironmentSpecConfig{
		//DockerSpecConfig specify the docker engine download repo
		DockerSpecConfig: DockerSpecConfig{
			DockerEngineRepo: "https://aptdocker.azureedge.net/repo",
		},
		//KubernetesSpecConfig is the default kubernetes container image url.
		KubernetesSpecConfig: KubernetesSpecConfig{
			KubernetesImageBase:    "gcrio.azureedge.net/google_containers/",
			TillerImageBase:        "gcrio.azureedge.net/kubernetes-helm/",
			KubeBinariesSASURLBase: "https://acs-mirror.azureedge.net/wink8s/",
		},

		DCOSSpecConfig: DCOSSpecConfig{
			DCOS173BootstrapDownloadURL: fmt.Sprintf(MsecndDCOSBootstrapDownloadURL, "testing", "df308b6fc3bd91e1277baa5a3db928ae70964722"),
			DCOS188BootstrapDownloadURL: fmt.Sprintf(AzureEdgeDCOSBootstrapDownloadURL, "stable", "5df43052907c021eeb5de145419a3da1898c58a5"),
			DCOS190BootstrapDownloadURL: fmt.Sprintf(AzureEdgeDCOSBootstrapDownloadURL, "stable", "58fd0833ce81b6244fc73bf65b5deb43217b0bd7"),
		},
	}

	//AzureChinaCloudSpec is the configurations for Azure China (Mooncake)
	AzureChinaCloudSpec = AzureEnvironmentSpecConfig{
		//DockerSpecConfig specify the docker engine download repo
		DockerSpecConfig: DockerSpecConfig{
			DockerEngineRepo: "https://mirror.azure.cn/docker-engine/apt/repo/",
		},
		//KubernetesSpecConfig - Due to Chinese firewall issue, the default containers from google is blocked, use the Chinese local mirror instead
		KubernetesSpecConfig: KubernetesSpecConfig{
			KubernetesImageBase:    "mirror.azure.cn:5000/google_containers/",
			KubeBinariesSASURLBase: "https://acs-mirror.azureedge.net/wink8s/",
		},
		DCOSSpecConfig: DCOSSpecConfig{
			DCOS173BootstrapDownloadURL: fmt.Sprintf(AzureChinaCloudDCOSBootstrapDownloadURL, "df308b6fc3bd91e1277baa5a3db928ae70964722"),
			DCOS188BootstrapDownloadURL: fmt.Sprintf(AzureChinaCloudDCOSBootstrapDownloadURL, "5df43052907c021eeb5de145419a3da1898c58a5"),
		},
	}
)

// SetPropertiesDefaults for the container Properties, returns true if certs are generated
func SetPropertiesDefaults(cs *api.ContainerService) (bool, error) {
	properties := cs.Properties

	setOrchestratorDefaults(cs)

	setMasterNetworkDefaults(properties)

	setAgentNetworkDefaults(properties)

	setStorageDefaults(properties)

	certsGenerated, e := setDefaultCerts(properties)
	if e != nil {
		return false, e
	}
	return certsGenerated, nil
}

// setOrchestratorDefaults for orchestrators
func setOrchestratorDefaults(cs *api.ContainerService) {
	location := cs.Location
	a := cs.Properties

	cloudSpecConfig := GetCloudSpecConfig(location)
	if a.OrchestratorProfile.OrchestratorType == api.Kubernetes {
		k8sRelease := a.OrchestratorProfile.OrchestratorRelease
		if a.OrchestratorProfile.KubernetesConfig == nil {
			a.OrchestratorProfile.KubernetesConfig = &api.KubernetesConfig{}
		}
		a.OrchestratorProfile.KubernetesConfig.KubernetesImageBase = cloudSpecConfig.KubernetesSpecConfig.KubernetesImageBase
		if a.OrchestratorProfile.KubernetesConfig.NetworkPolicy == "" {
			a.OrchestratorProfile.KubernetesConfig.NetworkPolicy = DefaultNetworkPolicy
		}
		if a.OrchestratorProfile.KubernetesConfig.ClusterSubnet == "" {
			if a.OrchestratorProfile.IsVNETIntegrated() {
				// When VNET integration is enabled, all masters, agents and pods share the same large subnet.
				a.OrchestratorProfile.KubernetesConfig.ClusterSubnet = DefaultKubernetesSubnet
			} else {
				a.OrchestratorProfile.KubernetesConfig.ClusterSubnet = DefaultKubernetesClusterSubnet
			}
		}
		if a.OrchestratorProfile.KubernetesConfig.DockerBridgeSubnet == "" {
			a.OrchestratorProfile.KubernetesConfig.DockerBridgeSubnet = DefaultDockerBridgeSubnet
		}
		if a.OrchestratorProfile.KubernetesConfig.NodeStatusUpdateFrequency == "" {
			a.OrchestratorProfile.KubernetesConfig.NodeStatusUpdateFrequency = KubeConfigs[k8sRelease]["nodestatusfreq"]
		}
		if a.OrchestratorProfile.KubernetesConfig.CtrlMgrNodeMonitorGracePeriod == "" {
			a.OrchestratorProfile.KubernetesConfig.CtrlMgrNodeMonitorGracePeriod = KubeConfigs[k8sRelease]["nodegraceperiod"]
		}
		if a.OrchestratorProfile.KubernetesConfig.CtrlMgrPodEvictionTimeout == "" {
			a.OrchestratorProfile.KubernetesConfig.CtrlMgrPodEvictionTimeout = KubeConfigs[k8sRelease]["podeviction"]
		}
		if a.OrchestratorProfile.KubernetesConfig.CtrlMgrRouteReconciliationPeriod == "" {
			a.OrchestratorProfile.KubernetesConfig.CtrlMgrRouteReconciliationPeriod = KubeConfigs[k8sRelease]["routeperiod"]
		}
		// Enforce sane cloudprovider backoff defaults, if CloudProviderBackoff is true in KubernetesConfig
		if a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoff == true {
			if a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffDuration == 0 {
				a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffDuration = DefaultKubernetesCloudProviderBackoffDuration
			}
			if a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffExponent == 0 {
				a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffExponent = DefaultKubernetesCloudProviderBackoffExponent
			}
			if a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffJitter == 0 {
				a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffJitter = DefaultKubernetesCloudProviderBackoffJitter
			}
			if a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffRetries == 0 {
				a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffRetries = DefaultKubernetesCloudProviderBackoffRetries
			}
		}
		k8sVersion, _ := semver.NewVersion(api.KubernetesReleaseToVersion[k8sRelease])
		constraint, _ := semver.NewConstraint(">= 1.6.6")
		// Enforce sane cloudprovider rate limit defaults, if CloudProviderRateLimit is true in KubernetesConfig
		// For k8s version greater or equal to 1.6.6, we will set the default CloudProviderRate* settings
		if a.OrchestratorProfile.KubernetesConfig.CloudProviderRateLimit == true && constraint.Check(k8sVersion) {
			if a.OrchestratorProfile.KubernetesConfig.CloudProviderRateLimitQPS == 0 {
				a.OrchestratorProfile.KubernetesConfig.CloudProviderRateLimitQPS = DefaultKubernetesCloudProviderRateLimitQPS
			}
			if a.OrchestratorProfile.KubernetesConfig.CloudProviderRateLimitBucket == 0 {
				a.OrchestratorProfile.KubernetesConfig.CloudProviderRateLimitBucket = DefaultKubernetesCloudProviderRateLimitBucket
			}
		}
	}
}

// SetMasterNetworkDefaults for masters
func setMasterNetworkDefaults(a *api.Properties) {
	if !a.MasterProfile.IsCustomVNET() {
		if a.OrchestratorProfile.OrchestratorType == api.Kubernetes {
			if a.OrchestratorProfile.IsVNETIntegrated() {
				// When VNET integration is enabled, all masters, agents and pods share the same large subnet.
				a.MasterProfile.Subnet = a.OrchestratorProfile.KubernetesConfig.ClusterSubnet
				a.MasterProfile.FirstConsecutiveStaticIP = getFirstConsecutiveStaticIPAddress(a.MasterProfile.Subnet)
			} else {
				a.MasterProfile.Subnet = DefaultKubernetesMasterSubnet
				a.MasterProfile.FirstConsecutiveStaticIP = DefaultFirstConsecutiveKubernetesStaticIP
			}
		} else if a.HasWindows() {
			a.MasterProfile.Subnet = DefaultSwarmWindowsMasterSubnet
			a.MasterProfile.FirstConsecutiveStaticIP = DefaultSwarmWindowsFirstConsecutiveStaticIP
		} else {
			a.MasterProfile.Subnet = DefaultMasterSubnet
			a.MasterProfile.FirstConsecutiveStaticIP = DefaultFirstConsecutiveStaticIP
		}
	}

	// Allocate IP addresses for containers if VNET integration is enabled.
	// A custom count specified by the user overrides this value.
	if a.MasterProfile.IPAddressCount == 0 {
		if a.OrchestratorProfile.IsVNETIntegrated() {
			a.MasterProfile.IPAddressCount = DefaultAgentMultiIPAddressCount
		} else {
			a.MasterProfile.IPAddressCount = DefaultAgentIPAddressCount
		}
	}

	if a.MasterProfile.HTTPSourceAddressPrefix == "" {
		a.MasterProfile.HTTPSourceAddressPrefix = "*"
	}
}

// SetAgentNetworkDefaults for agents
func setAgentNetworkDefaults(a *api.Properties) {
	// configure the subnets if not in custom VNET
	if !a.MasterProfile.IsCustomVNET() {
		subnetCounter := 0
		for _, profile := range a.AgentPoolProfiles {
			if a.OrchestratorProfile.OrchestratorType == api.Kubernetes {
				profile.Subnet = a.MasterProfile.Subnet
			} else {
				profile.Subnet = fmt.Sprintf(DefaultAgentSubnetTemplate, subnetCounter)
			}

			subnetCounter++
		}
	}

	for _, profile := range a.AgentPoolProfiles {
		// set default OSType to Linux
		if profile.OSType == "" {
			profile.OSType = api.Linux
		}

		// Allocate IP addresses for containers if VNET integration is enabled.
		// A custom count specified by the user overrides this value.
		if profile.IPAddressCount == 0 {
			if a.OrchestratorProfile.IsVNETIntegrated() {
				profile.IPAddressCount = DefaultAgentMultiIPAddressCount
			} else {
				profile.IPAddressCount = DefaultAgentIPAddressCount
			}
		}
	}
}

// setStorageDefaults for agents
func setStorageDefaults(a *api.Properties) {
	if len(a.MasterProfile.StorageProfile) == 0 {
		a.MasterProfile.StorageProfile = api.StorageAccount
	}
	for _, profile := range a.AgentPoolProfiles {
		if len(profile.StorageProfile) == 0 {
			profile.StorageProfile = api.StorageAccount
		}
		if len(profile.AvailabilityProfile) == 0 {
			profile.AvailabilityProfile = api.VirtualMachineScaleSets
		}
	}
}

func setDefaultCerts(a *api.Properties) (bool, error) {
	if !certGenerationRequired(a) {
		return false, nil
	}

	masterExtraFQDNs := FormatAzureProdFQDNs(a.MasterProfile.DNSPrefix)
	firstMasterIP := net.ParseIP(a.MasterProfile.FirstConsecutiveStaticIP).To4()

	if firstMasterIP == nil {
		return false, fmt.Errorf("MasterProfile.FirstConsecutiveStaticIP '%s' is an invalid IP address", a.MasterProfile.FirstConsecutiveStaticIP)
	}

	ips := []net.IP{firstMasterIP}

	// Add the Internal Loadbalancer IP which is always at at a known offset from the firstMasterIP
	ips = append(ips, net.IP{firstMasterIP[0], firstMasterIP[1], firstMasterIP[2], firstMasterIP[3] + byte(DefaultInternalLbStaticIPOffset)})

	// Include the Internal load balancer as well
	for i := 1; i < a.MasterProfile.Count; i++ {
		ip := net.IP{firstMasterIP[0], firstMasterIP[1], firstMasterIP[2], firstMasterIP[3] + byte(i)}
		ips = append(ips, ip)
	}

	if a.CertificateProfile == nil {
		a.CertificateProfile = &api.CertificateProfile{}
	}

	// use the specified Certificate Authority pair, or generate a new pair
	var caPair *PkiKeyCertPair
	if len(a.CertificateProfile.CaCertificate) != 0 && len(a.CertificateProfile.CaPrivateKey) != 0 {
		caPair = &PkiKeyCertPair{CertificatePem: a.CertificateProfile.CaCertificate, PrivateKeyPem: a.CertificateProfile.CaPrivateKey}
	} else {
		caCertificate, caPrivateKey, err := createCertificate("ca", nil, nil, false, nil, nil, nil)
		if err != nil {
			return false, err
		}
		caPair = &PkiKeyCertPair{CertificatePem: string(certificateToPem(caCertificate.Raw)), PrivateKeyPem: string(privateKeyToPem(caPrivateKey))}
		a.CertificateProfile.CaCertificate = caPair.CertificatePem
		a.CertificateProfile.CaPrivateKey = caPair.PrivateKeyPem
	}

	apiServerPair, clientPair, kubeConfigPair, err := CreatePki(masterExtraFQDNs, ips, DefaultKubernetesClusterDomain, caPair)
	if err != nil {
		return false, err
	}

	a.CertificateProfile.APIServerCertificate = apiServerPair.CertificatePem
	a.CertificateProfile.APIServerPrivateKey = apiServerPair.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 *api.Properties) bool {
	if certAlreadyPresent(a.CertificateProfile) {
		return false
	}

	switch a.OrchestratorProfile.OrchestratorType {
	case api.Kubernetes:
		return true
	default:
		return false
	}
}

// certAlreadyPresent determines if the passed in CertificateProfile includes certificate data
// TODO actually verify valid/useable certificate data
func certAlreadyPresent(c *api.CertificateProfile) bool {
	if c != nil {
		switch {
		case len(c.APIServerCertificate) > 0:
			return true
		case len(c.APIServerPrivateKey) > 0:
			return true
		case len(c.ClientCertificate) > 0:
			return true
		case len(c.ClientPrivateKey) > 0:
			return true
		default:
			return false
		}
	}
	return false
}

// getFirstConsecutiveStaticIPAddress returns the first static IP address of the given subnet.
func getFirstConsecutiveStaticIPAddress(subnetStr string) string {
	_, subnet, err := net.ParseCIDR(subnetStr)
	if err != nil {
		return DefaultFirstConsecutiveKubernetesStaticIP
	}

	// Find the first and last octet of the host bits.
	ones, bits := subnet.Mask.Size()
	firstOctet := ones / 8
	lastOctet := bits/8 - 1

	// Set the remaining host bits in the first octet.
	subnet.IP[firstOctet] |= (1 << byte((8 - (ones % 8)))) - 1

	// Fill the intermediate octets with 1s and last octet with offset. This is done so to match
	// the existing behavior of allocating static IP addresses from the last /24 of the subnet.
	for i := firstOctet + 1; i < lastOctet; i++ {
		subnet.IP[i] = 255
	}
	subnet.IP[lastOctet] = DefaultKubernetesFirstConsecutiveStaticIPOffset

	return subnet.IP.String()
}
<commit_msg>Add TillerImageBase for AzureChinaCloudSpec (#1246)<commit_after>package acsengine

import (
	"fmt"
	"net"

	"github.com/Azure/acs-engine/pkg/api"
	"github.com/Masterminds/semver"
)

var (
	//AzureCloudSpec is the default configurations for global azure.
	AzureCloudSpec = AzureEnvironmentSpecConfig{
		//DockerSpecConfig specify the docker engine download repo
		DockerSpecConfig: DockerSpecConfig{
			DockerEngineRepo: "https://aptdocker.azureedge.net/repo",
		},
		//KubernetesSpecConfig is the default kubernetes container image url.
		KubernetesSpecConfig: KubernetesSpecConfig{
			KubernetesImageBase:    "gcrio.azureedge.net/google_containers/",
			TillerImageBase:        "gcrio.azureedge.net/kubernetes-helm/",
			KubeBinariesSASURLBase: "https://acs-mirror.azureedge.net/wink8s/",
		},

		DCOSSpecConfig: DCOSSpecConfig{
			DCOS173BootstrapDownloadURL: fmt.Sprintf(MsecndDCOSBootstrapDownloadURL, "testing", "df308b6fc3bd91e1277baa5a3db928ae70964722"),
			DCOS188BootstrapDownloadURL: fmt.Sprintf(AzureEdgeDCOSBootstrapDownloadURL, "stable", "5df43052907c021eeb5de145419a3da1898c58a5"),
			DCOS190BootstrapDownloadURL: fmt.Sprintf(AzureEdgeDCOSBootstrapDownloadURL, "stable", "58fd0833ce81b6244fc73bf65b5deb43217b0bd7"),
		},
	}

	//AzureChinaCloudSpec is the configurations for Azure China (Mooncake)
	AzureChinaCloudSpec = AzureEnvironmentSpecConfig{
		//DockerSpecConfig specify the docker engine download repo
		DockerSpecConfig: DockerSpecConfig{
			DockerEngineRepo: "https://mirror.azure.cn/docker-engine/apt/repo/",
		},
		//KubernetesSpecConfig - Due to Chinese firewall issue, the default containers from google is blocked, use the Chinese local mirror instead
		KubernetesSpecConfig: KubernetesSpecConfig{
			KubernetesImageBase:    "mirror.azure.cn:5000/google_containers/",
			TillerImageBase:        "mirror.azure.cn:5000/kubernetes-helm/",
			KubeBinariesSASURLBase: "https://acs-mirror.azureedge.net/wink8s/",
		},
		DCOSSpecConfig: DCOSSpecConfig{
			DCOS173BootstrapDownloadURL: fmt.Sprintf(AzureChinaCloudDCOSBootstrapDownloadURL, "df308b6fc3bd91e1277baa5a3db928ae70964722"),
			DCOS188BootstrapDownloadURL: fmt.Sprintf(AzureChinaCloudDCOSBootstrapDownloadURL, "5df43052907c021eeb5de145419a3da1898c58a5"),
		},
	}
)

// SetPropertiesDefaults for the container Properties, returns true if certs are generated
func SetPropertiesDefaults(cs *api.ContainerService) (bool, error) {
	properties := cs.Properties

	setOrchestratorDefaults(cs)

	setMasterNetworkDefaults(properties)

	setAgentNetworkDefaults(properties)

	setStorageDefaults(properties)

	certsGenerated, e := setDefaultCerts(properties)
	if e != nil {
		return false, e
	}
	return certsGenerated, nil
}

// setOrchestratorDefaults for orchestrators
func setOrchestratorDefaults(cs *api.ContainerService) {
	location := cs.Location
	a := cs.Properties

	cloudSpecConfig := GetCloudSpecConfig(location)
	if a.OrchestratorProfile.OrchestratorType == api.Kubernetes {
		k8sRelease := a.OrchestratorProfile.OrchestratorRelease
		if a.OrchestratorProfile.KubernetesConfig == nil {
			a.OrchestratorProfile.KubernetesConfig = &api.KubernetesConfig{}
		}
		a.OrchestratorProfile.KubernetesConfig.KubernetesImageBase = cloudSpecConfig.KubernetesSpecConfig.KubernetesImageBase
		if a.OrchestratorProfile.KubernetesConfig.NetworkPolicy == "" {
			a.OrchestratorProfile.KubernetesConfig.NetworkPolicy = DefaultNetworkPolicy
		}
		if a.OrchestratorProfile.KubernetesConfig.ClusterSubnet == "" {
			if a.OrchestratorProfile.IsVNETIntegrated() {
				// When VNET integration is enabled, all masters, agents and pods share the same large subnet.
				a.OrchestratorProfile.KubernetesConfig.ClusterSubnet = DefaultKubernetesSubnet
			} else {
				a.OrchestratorProfile.KubernetesConfig.ClusterSubnet = DefaultKubernetesClusterSubnet
			}
		}
		if a.OrchestratorProfile.KubernetesConfig.DockerBridgeSubnet == "" {
			a.OrchestratorProfile.KubernetesConfig.DockerBridgeSubnet = DefaultDockerBridgeSubnet
		}
		if a.OrchestratorProfile.KubernetesConfig.NodeStatusUpdateFrequency == "" {
			a.OrchestratorProfile.KubernetesConfig.NodeStatusUpdateFrequency = KubeConfigs[k8sRelease]["nodestatusfreq"]
		}
		if a.OrchestratorProfile.KubernetesConfig.CtrlMgrNodeMonitorGracePeriod == "" {
			a.OrchestratorProfile.KubernetesConfig.CtrlMgrNodeMonitorGracePeriod = KubeConfigs[k8sRelease]["nodegraceperiod"]
		}
		if a.OrchestratorProfile.KubernetesConfig.CtrlMgrPodEvictionTimeout == "" {
			a.OrchestratorProfile.KubernetesConfig.CtrlMgrPodEvictionTimeout = KubeConfigs[k8sRelease]["podeviction"]
		}
		if a.OrchestratorProfile.KubernetesConfig.CtrlMgrRouteReconciliationPeriod == "" {
			a.OrchestratorProfile.KubernetesConfig.CtrlMgrRouteReconciliationPeriod = KubeConfigs[k8sRelease]["routeperiod"]
		}
		// Enforce sane cloudprovider backoff defaults, if CloudProviderBackoff is true in KubernetesConfig
		if a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoff == true {
			if a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffDuration == 0 {
				a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffDuration = DefaultKubernetesCloudProviderBackoffDuration
			}
			if a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffExponent == 0 {
				a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffExponent = DefaultKubernetesCloudProviderBackoffExponent
			}
			if a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffJitter == 0 {
				a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffJitter = DefaultKubernetesCloudProviderBackoffJitter
			}
			if a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffRetries == 0 {
				a.OrchestratorProfile.KubernetesConfig.CloudProviderBackoffRetries = DefaultKubernetesCloudProviderBackoffRetries
			}
		}
		k8sVersion, _ := semver.NewVersion(api.KubernetesReleaseToVersion[k8sRelease])
		constraint, _ := semver.NewConstraint(">= 1.6.6")
		// Enforce sane cloudprovider rate limit defaults, if CloudProviderRateLimit is true in KubernetesConfig
		// For k8s version greater or equal to 1.6.6, we will set the default CloudProviderRate* settings
		if a.OrchestratorProfile.KubernetesConfig.CloudProviderRateLimit == true && constraint.Check(k8sVersion) {
			if a.OrchestratorProfile.KubernetesConfig.CloudProviderRateLimitQPS == 0 {
				a.OrchestratorProfile.KubernetesConfig.CloudProviderRateLimitQPS = DefaultKubernetesCloudProviderRateLimitQPS
			}
			if a.OrchestratorProfile.KubernetesConfig.CloudProviderRateLimitBucket == 0 {
				a.OrchestratorProfile.KubernetesConfig.CloudProviderRateLimitBucket = DefaultKubernetesCloudProviderRateLimitBucket
			}
		}
	}
}

// SetMasterNetworkDefaults for masters
func setMasterNetworkDefaults(a *api.Properties) {
	if !a.MasterProfile.IsCustomVNET() {
		if a.OrchestratorProfile.OrchestratorType == api.Kubernetes {
			if a.OrchestratorProfile.IsVNETIntegrated() {
				// When VNET integration is enabled, all masters, agents and pods share the same large subnet.
				a.MasterProfile.Subnet = a.OrchestratorProfile.KubernetesConfig.ClusterSubnet
				a.MasterProfile.FirstConsecutiveStaticIP = getFirstConsecutiveStaticIPAddress(a.MasterProfile.Subnet)
			} else {
				a.MasterProfile.Subnet = DefaultKubernetesMasterSubnet
				a.MasterProfile.FirstConsecutiveStaticIP = DefaultFirstConsecutiveKubernetesStaticIP
			}
		} else if a.HasWindows() {
			a.MasterProfile.Subnet = DefaultSwarmWindowsMasterSubnet
			a.MasterProfile.FirstConsecutiveStaticIP = DefaultSwarmWindowsFirstConsecutiveStaticIP
		} else {
			a.MasterProfile.Subnet = DefaultMasterSubnet
			a.MasterProfile.FirstConsecutiveStaticIP = DefaultFirstConsecutiveStaticIP
		}
	}

	// Allocate IP addresses for containers if VNET integration is enabled.
	// A custom count specified by the user overrides this value.
	if a.MasterProfile.IPAddressCount == 0 {
		if a.OrchestratorProfile.IsVNETIntegrated() {
			a.MasterProfile.IPAddressCount = DefaultAgentMultiIPAddressCount
		} else {
			a.MasterProfile.IPAddressCount = DefaultAgentIPAddressCount
		}
	}

	if a.MasterProfile.HTTPSourceAddressPrefix == "" {
		a.MasterProfile.HTTPSourceAddressPrefix = "*"
	}
}

// SetAgentNetworkDefaults for agents
func setAgentNetworkDefaults(a *api.Properties) {
	// configure the subnets if not in custom VNET
	if !a.MasterProfile.IsCustomVNET() {
		subnetCounter := 0
		for _, profile := range a.AgentPoolProfiles {
			if a.OrchestratorProfile.OrchestratorType == api.Kubernetes {
				profile.Subnet = a.MasterProfile.Subnet
			} else {
				profile.Subnet = fmt.Sprintf(DefaultAgentSubnetTemplate, subnetCounter)
			}

			subnetCounter++
		}
	}

	for _, profile := range a.AgentPoolProfiles {
		// set default OSType to Linux
		if profile.OSType == "" {
			profile.OSType = api.Linux
		}

		// Allocate IP addresses for containers if VNET integration is enabled.
		// A custom count specified by the user overrides this value.
		if profile.IPAddressCount == 0 {
			if a.OrchestratorProfile.IsVNETIntegrated() {
				profile.IPAddressCount = DefaultAgentMultiIPAddressCount
			} else {
				profile.IPAddressCount = DefaultAgentIPAddressCount
			}
		}
	}
}

// setStorageDefaults for agents
func setStorageDefaults(a *api.Properties) {
	if len(a.MasterProfile.StorageProfile) == 0 {
		a.MasterProfile.StorageProfile = api.StorageAccount
	}
	for _, profile := range a.AgentPoolProfiles {
		if len(profile.StorageProfile) == 0 {
			profile.StorageProfile = api.StorageAccount
		}
		if len(profile.AvailabilityProfile) == 0 {
			profile.AvailabilityProfile = api.VirtualMachineScaleSets
		}
	}
}

func setDefaultCerts(a *api.Properties) (bool, error) {
	if !certGenerationRequired(a) {
		return false, nil
	}

	masterExtraFQDNs := FormatAzureProdFQDNs(a.MasterProfile.DNSPrefix)
	firstMasterIP := net.ParseIP(a.MasterProfile.FirstConsecutiveStaticIP).To4()

	if firstMasterIP == nil {
		return false, fmt.Errorf("MasterProfile.FirstConsecutiveStaticIP '%s' is an invalid IP address", a.MasterProfile.FirstConsecutiveStaticIP)
	}

	ips := []net.IP{firstMasterIP}

	// Add the Internal Loadbalancer IP which is always at at a known offset from the firstMasterIP
	ips = append(ips, net.IP{firstMasterIP[0], firstMasterIP[1], firstMasterIP[2], firstMasterIP[3] + byte(DefaultInternalLbStaticIPOffset)})

	// Include the Internal load balancer as well
	for i := 1; i < a.MasterProfile.Count; i++ {
		ip := net.IP{firstMasterIP[0], firstMasterIP[1], firstMasterIP[2], firstMasterIP[3] + byte(i)}
		ips = append(ips, ip)
	}

	if a.CertificateProfile == nil {
		a.CertificateProfile = &api.CertificateProfile{}
	}

	// use the specified Certificate Authority pair, or generate a new pair
	var caPair *PkiKeyCertPair
	if len(a.CertificateProfile.CaCertificate) != 0 && len(a.CertificateProfile.CaPrivateKey) != 0 {
		caPair = &PkiKeyCertPair{CertificatePem: a.CertificateProfile.CaCertificate, PrivateKeyPem: a.CertificateProfile.CaPrivateKey}
	} else {
		caCertificate, caPrivateKey, err := createCertificate("ca", nil, nil, false, nil, nil, nil)
		if err != nil {
			return false, err
		}
		caPair = &PkiKeyCertPair{CertificatePem: string(certificateToPem(caCertificate.Raw)), PrivateKeyPem: string(privateKeyToPem(caPrivateKey))}
		a.CertificateProfile.CaCertificate = caPair.CertificatePem
		a.CertificateProfile.CaPrivateKey = caPair.PrivateKeyPem
	}

	apiServerPair, clientPair, kubeConfigPair, err := CreatePki(masterExtraFQDNs, ips, DefaultKubernetesClusterDomain, caPair)
	if err != nil {
		return false, err
	}

	a.CertificateProfile.APIServerCertificate = apiServerPair.CertificatePem
	a.CertificateProfile.APIServerPrivateKey = apiServerPair.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 *api.Properties) bool {
	if certAlreadyPresent(a.CertificateProfile) {
		return false
	}

	switch a.OrchestratorProfile.OrchestratorType {
	case api.Kubernetes:
		return true
	default:
		return false
	}
}

// certAlreadyPresent determines if the passed in CertificateProfile includes certificate data
// TODO actually verify valid/useable certificate data
func certAlreadyPresent(c *api.CertificateProfile) bool {
	if c != nil {
		switch {
		case len(c.APIServerCertificate) > 0:
			return true
		case len(c.APIServerPrivateKey) > 0:
			return true
		case len(c.ClientCertificate) > 0:
			return true
		case len(c.ClientPrivateKey) > 0:
			return true
		default:
			return false
		}
	}
	return false
}

// getFirstConsecutiveStaticIPAddress returns the first static IP address of the given subnet.
func getFirstConsecutiveStaticIPAddress(subnetStr string) string {
	_, subnet, err := net.ParseCIDR(subnetStr)
	if err != nil {
		return DefaultFirstConsecutiveKubernetesStaticIP
	}

	// Find the first and last octet of the host bits.
	ones, bits := subnet.Mask.Size()
	firstOctet := ones / 8
	lastOctet := bits/8 - 1

	// Set the remaining host bits in the first octet.
	subnet.IP[firstOctet] |= (1 << byte((8 - (ones % 8)))) - 1

	// Fill the intermediate octets with 1s and last octet with offset. This is done so to match
	// the existing behavior of allocating static IP addresses from the last /24 of the subnet.
	for i := firstOctet + 1; i < lastOctet; i++ {
		subnet.IP[i] = 255
	}
	subnet.IP[lastOctet] = DefaultKubernetesFirstConsecutiveStaticIPOffset

	return subnet.IP.String()
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package setup contains common logic for setting up the export package across binaries.
package setup

import (
	"fmt"
	"os"
	"strconv"

	"cloud.google.com/go/compute/metadata"
	"github.com/GoogleCloudPlatform/prometheus-engine/pkg/export"
	"github.com/GoogleCloudPlatform/prometheus-engine/pkg/lease"
	"github.com/go-kit/kit/log"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	kingpin "gopkg.in/alecthomas/kingpin.v2"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"

	// Blank import required to register auth handlers to talk use different auth mechanisms
	// for talking to the Kubernetes API server.
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
)

// Supported HA backend modes.
const (
	HABackendNone       = "none"
	HABackendKubernetes = "kube"
)

// Generally, global state is not a good approach and actively discouraged throughout
// the Prometheus code bases. However, this is the most practical way to inject the export
// path into lower layers of Prometheus without touching an excessive amount of functions
// in our fork to propagate it.
var globalExporter *export.Exporter

// SetGlobal sets the global instance of the GCM exporter.
func SetGlobal(exporter *export.Exporter) (err error) {
	globalExporter = exporter
	return err
}

// Global returns the global instance of the GCM exporter.
func Global() *export.Exporter {
	if globalExporter == nil {
		// This should usually be a panic but we set an inactive default exporter in this case
		// to not break existing tests in Prometheus.
		fmt.Fprintln(os.Stderr, "No global GCM exporter was set, setting default inactive exporter.")
		return export.NopExporter()
	}
	return globalExporter
}

// FromFlags returns a constructor for a new exporter that is configured through flags that are
// registered with the given application. The constructor must be called after the flags
// have been parsed.
func FromFlags(a *kingpin.Application, userAgent string) func(log.Logger, prometheus.Registerer) (*export.Exporter, error) {
	var opts export.ExporterOpts

	// Default target fields if we can detect them in GCP.
	if metadata.OnGCE() {
		opts.ProjectID, _ = metadata.ProjectID()
		// These attributes are set for GKE nodes.
		opts.Location, _ = metadata.InstanceAttributeValue("cluster-location")
		opts.Cluster, _ = metadata.InstanceAttributeValue("cluster-name")
	}

	a.Flag("export.disable", "Disable exporting to GCM.").
		Default("false").BoolVar(&opts.Disable)

	a.Flag("export.endpoint", "GCM API endpoint to send metric data to.").
		Default("monitoring.googleapis.com:443").StringVar(&opts.Endpoint)

	a.Flag("export.credentials-file", "Credentials file for authentication with the GCM API.").
		Default("").StringVar(&opts.CredentialsFile)

	a.Flag("export.label.project-id", fmt.Sprintf("Default project ID set for all exported data. Prefer setting the external label %q in the Prometheus configuration if not using the auto-discovered default.", export.KeyProjectID)).
		Default(opts.ProjectID).StringVar(&opts.ProjectID)

	a.Flag("export.user-agent", "Override for the user agent used for requests against the GCM API.").
		Default(userAgent).StringVar(&opts.UserAgent)

	// The location and cluster flag should probably not be used. On the other hand, they make it easy
	// to populate these important values in the monitored resource without interfering with existing
	// Prometheus configuration.
	a.Flag("export.label.location", fmt.Sprintf("The default location set for all exported data. Prefer setting the external label %q in the Prometheus configuration if not using the auto-discovered default.", export.KeyLocation)).
		Default(opts.Location).StringVar(&opts.Location)

	a.Flag("export.label.cluster", fmt.Sprintf("The default cluster set for all scraped targets. Prefer setting the external label %q in the Prometheus configuration if not using the auto-discovered default.", export.KeyCluster)).
		Default(opts.Cluster).StringVar(&opts.Cluster)

	a.Flag("export.match", `A Prometheus time series matcher. Can be repeated. Every time series must match at least one of the matchers to be exported. This flag can be used equivalently to the match[] parameter of the Prometheus federation endpoint to selectively export data. (Example: --export.match='{job="prometheus"}' --export.match='{__name__=~"job:.*"})`).
		Default("").SetValue(&opts.Matchers)

	a.Flag("export.debug.metric-prefix", "Google Cloud Monitoring metric prefix to use.").
		Default(export.MetricTypePrefix).StringVar(&opts.MetricTypePrefix)

	a.Flag("export.debug.disable-auth", "Disable authentication (for debugging purposes).").
		Default("false").BoolVar(&opts.DisableAuth)

	a.Flag("export.debug.batch-size", "Maximum number of points to send in one batch to the GCM API.").
		Default(strconv.Itoa(export.BatchSizeMax)).UintVar(&opts.BatchSize)

	haBackend := a.Flag("export.ha.backend", fmt.Sprintf("Which backend to use to coordinate HA pairs that both send metric data to the GCM API. Valid values are %q or %q", HABackendNone, HABackendKubernetes)).
		Default(HABackendNone).Enum(HABackendNone, HABackendKubernetes)

	kubeConfigPath := a.Flag("export.ha.kube.config", "Path to kube config file.").
		Default("").String()
	kubeNamespace := a.Flag("export.ha.kube.namespace", "Namespace for the HA locking resource. Must be identical across replicas. May be set through the KUBE_NAMESPACE environment variable.").
		Default("").OverrideDefaultFromEnvar("KUBE_NAMESPACE").String()
	kubeName := a.Flag("export.ha.kube.name", "Name for the HA locking resource. Must be identical across replicas. May be set through the KUBE_NAME environment variable.").
		Default("").OverrideDefaultFromEnvar("KUBE_NAME").String()

	return func(logger log.Logger, metrics prometheus.Registerer) (*export.Exporter, error) {
		switch *haBackend {
		case HABackendNone:
		case HABackendKubernetes:
			kubecfg, err := loadKubeConfig(*kubeConfigPath)
			if err != nil {
				return nil, errors.Wrap(err, "loading kube config failed")
			}
			opts.Lease, err = lease.NewKubernetes(
				logger,
				metrics,
				kubecfg,
				*kubeNamespace, *kubeName,
				&lease.Options{},
			)
			if err != nil {
				return nil, errors.Wrap(err, "set up Kubernetes lease")
			}
		default:
			return nil, errors.Errorf("unexpected HA backend %q", haBackend)
		}
		return export.New(logger, metrics, opts)
	}
}

func loadKubeConfig(kubeconfigPath string) (*rest.Config, error) {
	if kubeconfigPath == "" {
		cfg, err := rest.InClusterConfig()
		if err == nil {
			return cfg, nil
		}
		// Fallback to default config.
	}
	rules := clientcmd.NewDefaultClientConfigLoadingRules()
	rules.ExplicitPath = kubeconfigPath

	return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, nil).ClientConfig()
}
<commit_msg>Default to instance zone if no cluster location is known<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package setup contains common logic for setting up the export package across binaries.
package setup

import (
	"fmt"
	"os"
	"strconv"

	"cloud.google.com/go/compute/metadata"
	"github.com/GoogleCloudPlatform/prometheus-engine/pkg/export"
	"github.com/GoogleCloudPlatform/prometheus-engine/pkg/lease"
	"github.com/go-kit/kit/log"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	kingpin "gopkg.in/alecthomas/kingpin.v2"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"

	// Blank import required to register auth handlers to talk use different auth mechanisms
	// for talking to the Kubernetes API server.
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
)

// Supported HA backend modes.
const (
	HABackendNone       = "none"
	HABackendKubernetes = "kube"
)

// Generally, global state is not a good approach and actively discouraged throughout
// the Prometheus code bases. However, this is the most practical way to inject the export
// path into lower layers of Prometheus without touching an excessive amount of functions
// in our fork to propagate it.
var globalExporter *export.Exporter

// SetGlobal sets the global instance of the GCM exporter.
func SetGlobal(exporter *export.Exporter) (err error) {
	globalExporter = exporter
	return err
}

// Global returns the global instance of the GCM exporter.
func Global() *export.Exporter {
	if globalExporter == nil {
		// This should usually be a panic but we set an inactive default exporter in this case
		// to not break existing tests in Prometheus.
		fmt.Fprintln(os.Stderr, "No global GCM exporter was set, setting default inactive exporter.")
		return export.NopExporter()
	}
	return globalExporter
}

// FromFlags returns a constructor for a new exporter that is configured through flags that are
// registered with the given application. The constructor must be called after the flags
// have been parsed.
func FromFlags(a *kingpin.Application, userAgent string) func(log.Logger, prometheus.Registerer) (*export.Exporter, error) {
	var opts export.ExporterOpts

	// Default target fields if we can detect them in GCP.
	if metadata.OnGCE() {
		opts.ProjectID, _ = metadata.ProjectID()
		opts.Location, _ = metadata.InstanceAttributeValue("cluster-location")
		// These attributes are set for GKE nodes. For the location, we first check
		// the clustr location, which may be a zone or a region. We must always use that value
		// to avoid collisions with other clusters, as the same cluster name may be reused
		// in different locations.
		// In particular, we cannot set the location to the node's zone for a regional cluster,
		// even though this would provide more accuracy, as there may also be a zonal cluster
		// with the same name.
		// We only fallback to the node zone as the location if no cluster location exists to
		// default for deployments on GCE.
		if loc, _ := metadata.InstanceAttributeValue("cluster-name"); loc != "" {
			opts.Location = loc
		} else {
			opts.Location, _ = metadata.Zone()
		}
	}

	a.Flag("export.disable", "Disable exporting to GCM.").
		Default("false").BoolVar(&opts.Disable)

	a.Flag("export.endpoint", "GCM API endpoint to send metric data to.").
		Default("monitoring.googleapis.com:443").StringVar(&opts.Endpoint)

	a.Flag("export.credentials-file", "Credentials file for authentication with the GCM API.").
		Default("").StringVar(&opts.CredentialsFile)

	a.Flag("export.label.project-id", fmt.Sprintf("Default project ID set for all exported data. Prefer setting the external label %q in the Prometheus configuration if not using the auto-discovered default.", export.KeyProjectID)).
		Default(opts.ProjectID).StringVar(&opts.ProjectID)

	a.Flag("export.user-agent", "Override for the user agent used for requests against the GCM API.").
		Default(userAgent).StringVar(&opts.UserAgent)

	// The location and cluster flag should probably not be used. On the other hand, they make it easy
	// to populate these important values in the monitored resource without interfering with existing
	// Prometheus configuration.
	a.Flag("export.label.location", fmt.Sprintf("The default location set for all exported data. Prefer setting the external label %q in the Prometheus configuration if not using the auto-discovered default.", export.KeyLocation)).
		Default(opts.Location).StringVar(&opts.Location)

	a.Flag("export.label.cluster", fmt.Sprintf("The default cluster set for all scraped targets. Prefer setting the external label %q in the Prometheus configuration if not using the auto-discovered default.", export.KeyCluster)).
		Default(opts.Cluster).StringVar(&opts.Cluster)

	a.Flag("export.match", `A Prometheus time series matcher. Can be repeated. Every time series must match at least one of the matchers to be exported. This flag can be used equivalently to the match[] parameter of the Prometheus federation endpoint to selectively export data. (Example: --export.match='{job="prometheus"}' --export.match='{__name__=~"job:.*"})`).
		Default("").SetValue(&opts.Matchers)

	a.Flag("export.debug.metric-prefix", "Google Cloud Monitoring metric prefix to use.").
		Default(export.MetricTypePrefix).StringVar(&opts.MetricTypePrefix)

	a.Flag("export.debug.disable-auth", "Disable authentication (for debugging purposes).").
		Default("false").BoolVar(&opts.DisableAuth)

	a.Flag("export.debug.batch-size", "Maximum number of points to send in one batch to the GCM API.").
		Default(strconv.Itoa(export.BatchSizeMax)).UintVar(&opts.BatchSize)

	haBackend := a.Flag("export.ha.backend", fmt.Sprintf("Which backend to use to coordinate HA pairs that both send metric data to the GCM API. Valid values are %q or %q", HABackendNone, HABackendKubernetes)).
		Default(HABackendNone).Enum(HABackendNone, HABackendKubernetes)

	kubeConfigPath := a.Flag("export.ha.kube.config", "Path to kube config file.").
		Default("").String()
	kubeNamespace := a.Flag("export.ha.kube.namespace", "Namespace for the HA locking resource. Must be identical across replicas. May be set through the KUBE_NAMESPACE environment variable.").
		Default("").OverrideDefaultFromEnvar("KUBE_NAMESPACE").String()
	kubeName := a.Flag("export.ha.kube.name", "Name for the HA locking resource. Must be identical across replicas. May be set through the KUBE_NAME environment variable.").
		Default("").OverrideDefaultFromEnvar("KUBE_NAME").String()

	return func(logger log.Logger, metrics prometheus.Registerer) (*export.Exporter, error) {
		switch *haBackend {
		case HABackendNone:
		case HABackendKubernetes:
			kubecfg, err := loadKubeConfig(*kubeConfigPath)
			if err != nil {
				return nil, errors.Wrap(err, "loading kube config failed")
			}
			opts.Lease, err = lease.NewKubernetes(
				logger,
				metrics,
				kubecfg,
				*kubeNamespace, *kubeName,
				&lease.Options{},
			)
			if err != nil {
				return nil, errors.Wrap(err, "set up Kubernetes lease")
			}
		default:
			return nil, errors.Errorf("unexpected HA backend %q", haBackend)
		}
		return export.New(logger, metrics, opts)
	}
}

func loadKubeConfig(kubeconfigPath string) (*rest.Config, error) {
	if kubeconfigPath == "" {
		cfg, err := rest.InClusterConfig()
		if err == nil {
			return cfg, nil
		}
		// Fallback to default config.
	}
	rules := clientcmd.NewDefaultClientConfigLoadingRules()
	rules.ExplicitPath = kubeconfigPath

	return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, nil).ClientConfig()
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gcpbuildpack

import (
	"os"

	"github.com/GoogleCloudPlatform/buildpacks/pkg/buildererror"
	"github.com/GoogleCloudPlatform/buildpacks/pkg/env"
	"github.com/buildpacks/libcnb"
)

const (
	layerMode os.FileMode = 0755
)

type layerOption func(ctx *Context, l *libcnb.Layer)

// BuildLayer specifies a Build layer.
var BuildLayer = func(ctx *Context, l *libcnb.Layer) { l.Build = true }

// CacheLayer specifies a Cache layer.
var CacheLayer = func(ctx *Context, l *libcnb.Layer) { l.Cache = true }

// LaunchLayer specifies a Launch layer.
var LaunchLayer = func(ctx *Context, l *libcnb.Layer) { l.Launch = true }

// LaunchLayerIfDevMode specifies a Launch layer, but only if dev mode is enabled.
var LaunchLayerIfDevMode = func(ctx *Context, l *libcnb.Layer) {
	devMode, err := env.IsDevMode()
	if err != nil {
		ctx.Warnf("Dev mode not enabled: %v", err)
		return
	}
	if devMode {
		l.Launch = true
	}
}

// LaunchLayerUnlessSkipRuntimeLaunch specifies a Launch layer unless XGoogleSkipRuntimeLaunch is set to "true".
var LaunchLayerUnlessSkipRuntimeLaunch = func(ctx *Context, l *libcnb.Layer) {
	skip, err := env.IsPresentAndTrue(env.XGoogleSkipRuntimeLaunch)
	if err != nil {
		ctx.Exit(1, buildererror.Errorf(buildererror.StatusInternal, err.Error()))

	}
	if !skip {
		l.Launch = true
	}
}

// Layer returns a layer, creating its directory.
func (ctx *Context) Layer(name string, opts ...layerOption) *libcnb.Layer {
	l, err := ctx.buildContext.Layers.Layer(name)
	if err != nil {
		ctx.Exit(1, buildererror.Errorf(buildererror.StatusInternal, err.Error()))
	}
	if err := ctx.MkdirAll(l.Path, layerMode); err != nil {
		ctx.Exit(1, buildererror.Errorf(buildererror.StatusInternal, "creating %s: %v", l.Path, err))
	}
	for _, o := range opts {
		o(ctx, &l)
	}
	if l.Metadata == nil {
		l.Metadata = make(map[string]interface{})
	}
	ctx.buildResult.Layers = append(ctx.buildResult.Layers, layerContributor{&l})
	return &l
}

type layerContributor struct {
	l *libcnb.Layer
}

// Contribute accepts a layer and transforms it, returning a layer.
func (lc layerContributor) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
	return *lc.l, nil
}

// Name is the name of the layer.
func (lc layerContributor) Name() string {
	return lc.l.Name
}

// ClearLayer erases the existing layer, and re-creates the directory.
func (ctx *Context) ClearLayer(l *libcnb.Layer) {
	ctx.RemoveAll(l.Path)
	if err := ctx.MkdirAll(l.Path, layerMode); err != nil {
		ctx.Exit(1, buildererror.Errorf(buildererror.StatusInternal, "creating %s: %v", l.Path, err))
	}
}

// SetMetadata sets metadata on the layer.
func (ctx *Context) SetMetadata(l *libcnb.Layer, key, value string) {
	l.Metadata[key] = value
}

// GetMetadata gets metadata from the layer.
func (ctx *Context) GetMetadata(l *libcnb.Layer, key string) string {
	v, ok := l.Metadata[key]
	if !ok {
		return ""
	}
	s, ok := v.(string)
	if !ok {
		ctx.Exit(1, buildererror.Errorf(buildererror.StatusInternal, "could not cast metadata %v to string", v))
	}
	return s
}
<commit_msg>Change layer option functions to return error instead of exiting<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gcpbuildpack

import (
	"errors"
	"os"

	"github.com/GoogleCloudPlatform/buildpacks/pkg/buildererror"
	"github.com/GoogleCloudPlatform/buildpacks/pkg/env"
	"github.com/buildpacks/libcnb"
)

const (
	layerMode os.FileMode = 0755
)

type layerOption func(ctx *Context, l *libcnb.Layer) error

// BuildLayer specifies a Build layer.
var BuildLayer = func(ctx *Context, l *libcnb.Layer) error {
	l.Build = true
	return nil
}

// CacheLayer specifies a Cache layer.
var CacheLayer = func(ctx *Context, l *libcnb.Layer) error {
	l.Cache = true
	return nil
}

// LaunchLayer specifies a Launch layer.
var LaunchLayer = func(ctx *Context, l *libcnb.Layer) error {
	l.Launch = true
	return nil
}

// LaunchLayerIfDevMode specifies a Launch layer, but only if dev mode is enabled.
var LaunchLayerIfDevMode = func(ctx *Context, l *libcnb.Layer) error {
	devMode, err := env.IsDevMode()
	if err != nil {
		ctx.Warnf("Dev mode not enabled: %v", err)
		return nil
	}
	if devMode {
		l.Launch = true
	}
	return nil
}

// LaunchLayerUnlessSkipRuntimeLaunch specifies a Launch layer unless XGoogleSkipRuntimeLaunch is set to "true".
var LaunchLayerUnlessSkipRuntimeLaunch = func(ctx *Context, l *libcnb.Layer) error {
	skip, err := env.IsPresentAndTrue(env.XGoogleSkipRuntimeLaunch)
	if err != nil {
		return buildererror.Errorf(buildererror.StatusInternal, err.Error())
	}
	if !skip {
		l.Launch = true
	}
	return nil
}

// Layer returns a layer, creating its directory.
func (ctx *Context) Layer(name string, opts ...layerOption) *libcnb.Layer {
	l, err := ctx.buildContext.Layers.Layer(name)
	if err != nil {
		ctx.Exit(1, buildererror.Errorf(buildererror.StatusInternal, err.Error()))
	}
	if err := ctx.MkdirAll(l.Path, layerMode); err != nil {
		ctx.Exit(1, buildererror.Errorf(buildererror.StatusInternal, "creating %s: %v", l.Path, err))
	}
	for _, o := range opts {
		if err := o(ctx, &l); err != nil {
			var be buildererror.Error
			if errors.As(err, &be) {
				ctx.Exit(1, &be)
			}
			ctx.Exit(1, buildererror.Errorf(buildererror.StatusInternal, "applying layer option: %v", err))
		}
	}
	if l.Metadata == nil {
		l.Metadata = make(map[string]interface{})
	}
	ctx.buildResult.Layers = append(ctx.buildResult.Layers, layerContributor{&l})
	return &l
}

type layerContributor struct {
	l *libcnb.Layer
}

// Contribute accepts a layer and transforms it, returning a layer.
func (lc layerContributor) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
	return *lc.l, nil
}

// Name is the name of the layer.
func (lc layerContributor) Name() string {
	return lc.l.Name
}

// ClearLayer erases the existing layer, and re-creates the directory.
func (ctx *Context) ClearLayer(l *libcnb.Layer) {
	ctx.RemoveAll(l.Path)
	if err := ctx.MkdirAll(l.Path, layerMode); err != nil {
		ctx.Exit(1, buildererror.Errorf(buildererror.StatusInternal, "creating %s: %v", l.Path, err))
	}
}

// SetMetadata sets metadata on the layer.
func (ctx *Context) SetMetadata(l *libcnb.Layer, key, value string) {
	l.Metadata[key] = value
}

// GetMetadata gets metadata from the layer.
func (ctx *Context) GetMetadata(l *libcnb.Layer, key string) string {
	v, ok := l.Metadata[key]
	if !ok {
		return ""
	}
	s, ok := v.(string)
	if !ok {
		ctx.Exit(1, buildererror.Errorf(buildererror.StatusInternal, "could not cast metadata %v to string", v))
	}
	return s
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
	"fmt"
	"io"
	"strings"

	"github.com/spf13/cobra"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
	cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)

const (
	create_long = `Create a resource by filename or stdin.

JSON and YAML formats are accepted.`
	create_example = `// Create a pod using the data in pod.json.
$ kubectl create -f pod.json

// Create a pod based on the JSON passed into stdin.
$ cat pod.json | kubectl create -f -`
)

func NewCmdCreate(f *cmdutil.Factory, out io.Writer) *cobra.Command {
	var filenames util.StringList
	cmd := &cobra.Command{
		Use:     "create -f FILENAME",
		Short:   "Create a resource by filename or stdin",
		Long:    create_long,
		Example: create_example,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(ValidateArgs(cmd, args))
			cmdutil.CheckErr(RunCreate(f, out, filenames))
		},
	}

	usage := "Filename, directory, or URL to file to use to create the resource"
	kubectl.AddJsonFilenameFlag(cmd, &filenames, usage)
	cmd.MarkFlagRequired("filename")

	return cmd
}

func ValidateArgs(cmd *cobra.Command, args []string) error {
	if len(args) != 0 {
		return cmdutil.UsageError(cmd, "Unexpected args: %v", args)
	}
	return nil
}

func RunCreate(f *cmdutil.Factory, out io.Writer, filenames util.StringList) error {
	schema, err := f.Validator()
	if err != nil {
		return err
	}

	cmdNamespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	mapper, typer := f.Object()
	r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
		Schema(schema).
		ContinueOnError().
		NamespaceParam(cmdNamespace).RequireNamespace().
		FilenameParam(filenames...).
		Flatten().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}

	count := 0
	err = r.Visit(func(info *resource.Info) error {
		data, err := info.Mapping.Codec.Encode(info.Object)
		if err != nil {
			return err
		}
		obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, data)
		if err != nil {
			return err
		}
		count++
		info.Refresh(obj, true)
		printObjectSpecificMessage(info.Object, out)
		fmt.Fprintf(out, "%s/%s\n", info.Mapping.Resource, info.Name)
		return nil
	})
	if err != nil {
		return err
	}
	if count == 0 {
		return fmt.Errorf("no objects passed to create")
	}
	return nil
}

func printObjectSpecificMessage(obj runtime.Object, out io.Writer) {
	switch obj := obj.(type) {
	case *api.Service:
		if obj.Spec.Type == api.ServiceTypeLoadBalancer {
			msg := fmt.Sprintf(`
			An external load-balanced service was created.  On many platforms (e.g. Google Compute Engine),
			you will also need to explicitly open a Firewall rule for the service port(s) (%s) to serve traffic.

			See https://github.com/GoogleCloudPlatform/kubernetes/tree/master/docs/services-firewall.md for more details.
			`, makePortsString(obj.Spec.Ports))
			out.Write([]byte(msg))
		}
		if obj.Spec.Type == api.ServiceTypeNodePort {
			msg := fmt.Sprintf(`
				You have exposed your service on an external port on all nodes in your cluster.
				If you want to expose this service to the external internet, you may need to set up
				firewall rules for the service port(s) (%s) to serve traffic.

				See https://github.com/GoogleCloudPlatform/kubernetes/tree/master/docs/services-firewall.md for more details.
				`, makePortsString(obj.Spec.Ports))
			out.Write([]byte(msg))
		}
	}
}

func makePortsString(ports []api.ServicePort) string {
	pieces := make([]string, len(ports))
	for ix := range ports {
		pieces[ix] = fmt.Sprintf("%s:%d", strings.ToLower(string(ports[ix].Protocol)), ports[ix].Port)
	}
	return strings.Join(pieces, ",")
}
<commit_msg>Use NodePort instead Port, firewalls install firewall.<commit_after>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
	"fmt"
	"io"
	"strings"

	"github.com/spf13/cobra"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
	cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)

const (
	create_long = `Create a resource by filename or stdin.

JSON and YAML formats are accepted.`
	create_example = `// Create a pod using the data in pod.json.
$ kubectl create -f pod.json

// Create a pod based on the JSON passed into stdin.
$ cat pod.json | kubectl create -f -`
)

func NewCmdCreate(f *cmdutil.Factory, out io.Writer) *cobra.Command {
	var filenames util.StringList
	cmd := &cobra.Command{
		Use:     "create -f FILENAME",
		Short:   "Create a resource by filename or stdin",
		Long:    create_long,
		Example: create_example,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(ValidateArgs(cmd, args))
			cmdutil.CheckErr(RunCreate(f, out, filenames))
		},
	}

	usage := "Filename, directory, or URL to file to use to create the resource"
	kubectl.AddJsonFilenameFlag(cmd, &filenames, usage)
	cmd.MarkFlagRequired("filename")

	return cmd
}

func ValidateArgs(cmd *cobra.Command, args []string) error {
	if len(args) != 0 {
		return cmdutil.UsageError(cmd, "Unexpected args: %v", args)
	}
	return nil
}

func RunCreate(f *cmdutil.Factory, out io.Writer, filenames util.StringList) error {
	schema, err := f.Validator()
	if err != nil {
		return err
	}

	cmdNamespace, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	mapper, typer := f.Object()
	r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
		Schema(schema).
		ContinueOnError().
		NamespaceParam(cmdNamespace).RequireNamespace().
		FilenameParam(filenames...).
		Flatten().
		Do()
	err = r.Err()
	if err != nil {
		return err
	}

	count := 0
	err = r.Visit(func(info *resource.Info) error {
		data, err := info.Mapping.Codec.Encode(info.Object)
		if err != nil {
			return err
		}
		obj, err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, data)
		if err != nil {
			return err
		}
		count++
		info.Refresh(obj, true)
		printObjectSpecificMessage(info.Object, out)
		fmt.Fprintf(out, "%s/%s\n", info.Mapping.Resource, info.Name)
		return nil
	})
	if err != nil {
		return err
	}
	if count == 0 {
		return fmt.Errorf("no objects passed to create")
	}
	return nil
}

func printObjectSpecificMessage(obj runtime.Object, out io.Writer) {
	switch obj := obj.(type) {
	case *api.Service:
		if obj.Spec.Type == api.ServiceTypeLoadBalancer {
			msg := fmt.Sprintf(`
			An external load-balanced service was created.  On many platforms (e.g. Google Compute Engine),
			you will also need to explicitly open a Firewall rule for the service port(s) (%s) to serve traffic.

			See https://github.com/GoogleCloudPlatform/kubernetes/tree/master/docs/services-firewalls.md for more details.
			`, makePortsString(obj.Spec.Ports))
			out.Write([]byte(msg))
		}
		if obj.Spec.Type == api.ServiceTypeNodePort {
			msg := fmt.Sprintf(`
				You have exposed your service on an external port on all nodes in your cluster.
				If you want to expose this service to the external internet, you may need to set up
				firewall rules for the service port(s) (%s) to serve traffic.

				See https://github.com/GoogleCloudPlatform/kubernetes/tree/master/docs/services-firewalls.md for more details.
				`, makePortsString(obj.Spec.Ports))
			out.Write([]byte(msg))
		}
	}
}

func makePortsString(ports []api.ServicePort) string {
	pieces := make([]string, len(ports))
	for ix := range ports {
		pieces[ix] = fmt.Sprintf("%s:%d", strings.ToLower(string(ports[ix].Protocol)), ports[ix].NodePort)
	}
	return strings.Join(pieces, ",")
}
<|endoftext|>


<commit_before>package libcontainer

import (
	"errors"

	"github.com/syndtr/gocapability/capability"
)

var (
	ErrUnkownNamespace  = errors.New("Unknown namespace")
	ErrUnkownCapability = errors.New("Unknown capability")
	ErrUnsupported      = errors.New("Unsupported method")
)

type Mounts []Mount

func (s Mounts) OfType(t string) Mounts {
	out := Mounts{}
	for _, m := range s {
		if m.Type == t {
			out = append(out, m)
		}
	}
	return out
}

type Mount struct {
	Type        string `json:"type,omitempty"`
	Source      string `json:"source,omitempty"`      // Source path, in the host namespace
	Destination string `json:"destination,omitempty"` // Destination path, in the container
	Writable    bool   `json:"writable,omitempty"`
	Private     bool   `json:"private,omitempty"`
}

// namespaceList is used to convert the libcontainer types
// into the names of the files located in /proc/<pid>/ns/* for
// each namespace
var (
	namespaceList = Namespaces{}

	capabilityList = Capabilities{
		{Key: "SETPCAP", Value: capability.CAP_SETPCAP},
		{Key: "SYS_MODULE", Value: capability.CAP_SYS_MODULE},
		{Key: "SYS_RAWIO", Value: capability.CAP_SYS_RAWIO},
		{Key: "SYS_PACCT", Value: capability.CAP_SYS_PACCT},
		{Key: "SYS_ADMIN", Value: capability.CAP_SYS_ADMIN},
		{Key: "SYS_NICE", Value: capability.CAP_SYS_NICE},
		{Key: "SYS_RESOURCE", Value: capability.CAP_SYS_RESOURCE},
		{Key: "SYS_TIME", Value: capability.CAP_SYS_TIME},
		{Key: "SYS_TTY_CONFIG", Value: capability.CAP_SYS_TTY_CONFIG},
		{Key: "MKNOD", Value: capability.CAP_MKNOD},
		{Key: "AUDIT_WRITE", Value: capability.CAP_AUDIT_WRITE},
		{Key: "AUDIT_CONTROL", Value: capability.CAP_AUDIT_CONTROL},
		{Key: "MAC_OVERRIDE", Value: capability.CAP_MAC_OVERRIDE},
		{Key: "MAC_ADMIN", Value: capability.CAP_MAC_ADMIN},
		{Key: "NET_ADMIN", Value: capability.CAP_NET_ADMIN},
		{Key: "SYSLOG", Value: capability.CAP_SYSLOG},
		{Key: "SETUID", Value: capability.CAP_SETUID},
		{Key: "SETGID", Value: capability.CAP_SETGID},
		{Key: "CHOWN", Value: capability.CAP_CHOWN},
		{Key: "NET_RAW", Value: capability.CAP_NET_RAW},
		{Key: "DAC_OVERRIDE", Value: capability.CAP_DAC_OVERRIDE},
	}
)

type (
	Namespace struct {
		Key   string `json:"key,omitempty"`
		Value int    `json:"value,omitempty"`
		File  string `json:"file,omitempty"`
	}
	Namespaces []*Namespace
)

func (ns *Namespace) String() string {
	return ns.Key
}

func GetNamespace(key string) *Namespace {
	for _, ns := range namespaceList {
		if ns.Key == key {
			cpy := *ns
			return &cpy
		}
	}
	return nil
}

// Contains returns true if the specified Namespace is
// in the slice
func (n Namespaces) Contains(ns string) bool {
	return n.Get(ns) != nil
}

func (n Namespaces) Get(ns string) *Namespace {
	for _, nsp := range n {
		if nsp != nil && nsp.Key == ns {
			return nsp
		}
	}
	return nil
}

type (
	Capability struct {
		Key   string         `json:"key,omitempty"`
		Value capability.Cap `json:"value,omitempty"`
	}
	Capabilities []*Capability
)

func (c *Capability) String() string {
	return c.Key
}

func GetCapability(key string) *Capability {
	for _, capp := range capabilityList {
		if capp.Key == key {
			cpy := *capp
			return &cpy
		}
	}
	return nil
}

func GetAllCapabilities() []string {
	output := make([]string, len(capabilityList))
	for i, capability := range capabilityList {
		output[i] = capability.String()
	}
	return output
}

// Contains returns true if the specified Capability is
// in the slice
func (c Capabilities) Contains(capp string) bool {
	return c.Get(capp) != nil
}

func (c Capabilities) Get(capp string) *Capability {
	for _, cap := range c {
		if cap.Key == capp {
			return cap
		}
	}
	return nil
}
<commit_msg>add support for CAP_FOWNER<commit_after>package libcontainer

import (
	"errors"

	"github.com/syndtr/gocapability/capability"
)

var (
	ErrUnkownNamespace  = errors.New("Unknown namespace")
	ErrUnkownCapability = errors.New("Unknown capability")
	ErrUnsupported      = errors.New("Unsupported method")
)

type Mounts []Mount

func (s Mounts) OfType(t string) Mounts {
	out := Mounts{}
	for _, m := range s {
		if m.Type == t {
			out = append(out, m)
		}
	}
	return out
}

type Mount struct {
	Type        string `json:"type,omitempty"`
	Source      string `json:"source,omitempty"`      // Source path, in the host namespace
	Destination string `json:"destination,omitempty"` // Destination path, in the container
	Writable    bool   `json:"writable,omitempty"`
	Private     bool   `json:"private,omitempty"`
}

// namespaceList is used to convert the libcontainer types
// into the names of the files located in /proc/<pid>/ns/* for
// each namespace
var (
	namespaceList = Namespaces{}

	capabilityList = Capabilities{
		{Key: "SETPCAP", Value: capability.CAP_SETPCAP},
		{Key: "SYS_MODULE", Value: capability.CAP_SYS_MODULE},
		{Key: "SYS_RAWIO", Value: capability.CAP_SYS_RAWIO},
		{Key: "SYS_PACCT", Value: capability.CAP_SYS_PACCT},
		{Key: "SYS_ADMIN", Value: capability.CAP_SYS_ADMIN},
		{Key: "SYS_NICE", Value: capability.CAP_SYS_NICE},
		{Key: "SYS_RESOURCE", Value: capability.CAP_SYS_RESOURCE},
		{Key: "SYS_TIME", Value: capability.CAP_SYS_TIME},
		{Key: "SYS_TTY_CONFIG", Value: capability.CAP_SYS_TTY_CONFIG},
		{Key: "MKNOD", Value: capability.CAP_MKNOD},
		{Key: "AUDIT_WRITE", Value: capability.CAP_AUDIT_WRITE},
		{Key: "AUDIT_CONTROL", Value: capability.CAP_AUDIT_CONTROL},
		{Key: "MAC_OVERRIDE", Value: capability.CAP_MAC_OVERRIDE},
		{Key: "MAC_ADMIN", Value: capability.CAP_MAC_ADMIN},
		{Key: "NET_ADMIN", Value: capability.CAP_NET_ADMIN},
		{Key: "SYSLOG", Value: capability.CAP_SYSLOG},
		{Key: "SETUID", Value: capability.CAP_SETUID},
		{Key: "SETGID", Value: capability.CAP_SETGID},
		{Key: "CHOWN", Value: capability.CAP_CHOWN},
		{Key: "NET_RAW", Value: capability.CAP_NET_RAW},
		{Key: "DAC_OVERRIDE", Value: capability.CAP_DAC_OVERRIDE},
		{Key: "FOWNER", Value: capability.CAP_FOWNER},
	}
)

type (
	Namespace struct {
		Key   string `json:"key,omitempty"`
		Value int    `json:"value,omitempty"`
		File  string `json:"file,omitempty"`
	}
	Namespaces []*Namespace
)

func (ns *Namespace) String() string {
	return ns.Key
}

func GetNamespace(key string) *Namespace {
	for _, ns := range namespaceList {
		if ns.Key == key {
			cpy := *ns
			return &cpy
		}
	}
	return nil
}

// Contains returns true if the specified Namespace is
// in the slice
func (n Namespaces) Contains(ns string) bool {
	return n.Get(ns) != nil
}

func (n Namespaces) Get(ns string) *Namespace {
	for _, nsp := range n {
		if nsp != nil && nsp.Key == ns {
			return nsp
		}
	}
	return nil
}

type (
	Capability struct {
		Key   string         `json:"key,omitempty"`
		Value capability.Cap `json:"value,omitempty"`
	}
	Capabilities []*Capability
)

func (c *Capability) String() string {
	return c.Key
}

func GetCapability(key string) *Capability {
	for _, capp := range capabilityList {
		if capp.Key == key {
			cpy := *capp
			return &cpy
		}
	}
	return nil
}

func GetAllCapabilities() []string {
	output := make([]string, len(capabilityList))
	for i, capability := range capabilityList {
		output[i] = capability.String()
	}
	return output
}

// Contains returns true if the specified Capability is
// in the slice
func (c Capabilities) Contains(capp string) bool {
	return c.Get(capp) != nil
}

func (c Capabilities) Get(capp string) *Capability {
	for _, cap := range c {
		if cap.Key == capp {
			return cap
		}
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package plugin // import "helm.sh/helm/v3/pkg/plugin"

import (
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"testing"

	"helm.sh/helm/v3/pkg/cli"
)

func checkCommand(p *Plugin, extraArgs []string, osStrCmp string, t *testing.T) {
	cmd, args, err := p.PrepareCommand(extraArgs)
	if err != nil {
		t.Errorf(err.Error())
	}
	if cmd != "echo" {
		t.Errorf("Expected echo, got %q", cmd)
	}

	if l := len(args); l != 5 {
		t.Errorf("expected 5 args, got %d", l)
	}

	expect := []string{"-n", osStrCmp, "--debug", "--foo", "bar"}
	for i := 0; i < len(args); i++ {
		if expect[i] != args[i] {
			t.Errorf("Expected arg=%q, got %q", expect[i], args[i])
		}
	}

	// Test with IgnoreFlags. This should omit --debug, --foo, bar
	p.Metadata.IgnoreFlags = true
	cmd, args, err = p.PrepareCommand(extraArgs)
	if err != nil {
		t.Errorf(err.Error())
	}
	if cmd != "echo" {
		t.Errorf("Expected echo, got %q", cmd)
	}
	if l := len(args); l != 2 {
		t.Errorf("expected 2 args, got %d", l)
	}
	expect = []string{"-n", osStrCmp}
	for i := 0; i < len(args); i++ {
		if expect[i] != args[i] {
			t.Errorf("Expected arg=%q, got %q", expect[i], args[i])
		}
	}
}

func TestPrepareCommand(t *testing.T) {
	p := &Plugin{
		Dir: "/tmp", // Unused
		Metadata: &Metadata{
			Name:    "test",
			Command: "echo -n foo",
		},
	}
	argv := []string{"--debug", "--foo", "bar"}

	checkCommand(p, argv, "foo", t)
}

func TestPlatformPrepareCommand(t *testing.T) {
	p := &Plugin{
		Dir: "/tmp", // Unused
		Metadata: &Metadata{
			Name:    "test",
			Command: "echo -n os-arch",
			PlatformCommand: []PlatformCommand{
				{OperatingSystem: "linux", Architecture: "i386", Command: "echo -n linux-i386"},
				{OperatingSystem: "linux", Architecture: "amd64", Command: "echo -n linux-amd64"},
				{OperatingSystem: "linux", Architecture: "s390x", Command: "echo -n linux-s390x"},
				{OperatingSystem: "windows", Architecture: "amd64", Command: "echo -n win-64"},
			},
		},
	}
	var osStrCmp string
	os := runtime.GOOS
	arch := runtime.GOARCH
	if os == "linux" && arch == "i386" {
		osStrCmp = "linux-i386"
	} else if os == "linux" && arch == "amd64" {
		osStrCmp = "linux-amd64"
	} else if os == "linux" && arch == "s390x" {
		osStrCmp = "linux-s390x"
	} else if os == "windows" && arch == "amd64" {
		osStrCmp = "win-64"
	} else {
		osStrCmp = "os-arch"
	}

	argv := []string{"--debug", "--foo", "bar"}
	checkCommand(p, argv, osStrCmp, t)
}

func TestPartialPlatformPrepareCommand(t *testing.T) {
	p := &Plugin{
		Dir: "/tmp", // Unused
		Metadata: &Metadata{
			Name:    "test",
			Command: "echo -n os-arch",
			PlatformCommand: []PlatformCommand{
				{OperatingSystem: "linux", Architecture: "i386", Command: "echo -n linux-i386"},
				{OperatingSystem: "windows", Architecture: "amd64", Command: "echo -n win-64"},
			},
		},
	}
	var osStrCmp string
	os := runtime.GOOS
	arch := runtime.GOARCH
	if os == "linux" {
		osStrCmp = "linux-i386"
	} else if os == "windows" && arch == "amd64" {
		osStrCmp = "win-64"
	} else {
		osStrCmp = "os-arch"
	}

	argv := []string{"--debug", "--foo", "bar"}
	checkCommand(p, argv, osStrCmp, t)
}

func TestNoPrepareCommand(t *testing.T) {
	p := &Plugin{
		Dir: "/tmp", // Unused
		Metadata: &Metadata{
			Name: "test",
		},
	}
	argv := []string{"--debug", "--foo", "bar"}

	_, _, err := p.PrepareCommand(argv)
	if err == nil {
		t.Errorf("Expected error to be returned")
	}
}

func TestNoMatchPrepareCommand(t *testing.T) {
	p := &Plugin{
		Dir: "/tmp", // Unused
		Metadata: &Metadata{
			Name: "test",
			PlatformCommand: []PlatformCommand{
				{OperatingSystem: "no-os", Architecture: "amd64", Command: "echo -n linux-i386"},
			},
		},
	}
	argv := []string{"--debug", "--foo", "bar"}

	if _, _, err := p.PrepareCommand(argv); err == nil {
		t.Errorf("Expected error to be returned")
	}
}

func TestLoadDir(t *testing.T) {
	dirname := "testdata/plugdir/hello"
	plug, err := LoadDir(dirname)
	if err != nil {
		t.Fatalf("error loading Hello plugin: %s", err)
	}

	if plug.Dir != dirname {
		t.Errorf("Expected dir %q, got %q", dirname, plug.Dir)
	}

	expect := &Metadata{
		Name:        "hello",
		Version:     "0.1.0",
		Usage:       "usage",
		Description: "description",
		Command:     "$HELM_PLUGIN_SELF/hello.sh",
		IgnoreFlags: true,
		Hooks: map[string]string{
			Install: "echo installing...",
		},
	}

	if !reflect.DeepEqual(expect, plug.Metadata) {
		t.Errorf("Expected plugin metadata %v, got %v", expect, plug.Metadata)
	}
}

func TestDownloader(t *testing.T) {
	dirname := "testdata/plugdir/downloader"
	plug, err := LoadDir(dirname)
	if err != nil {
		t.Fatalf("error loading Hello plugin: %s", err)
	}

	if plug.Dir != dirname {
		t.Errorf("Expected dir %q, got %q", dirname, plug.Dir)
	}

	expect := &Metadata{
		Name:        "downloader",
		Version:     "1.2.3",
		Usage:       "usage",
		Description: "download something",
		Command:     "echo Hello",
		Downloaders: []Downloaders{
			{
				Protocols: []string{"myprotocol", "myprotocols"},
				Command:   "echo Download",
			},
		},
	}

	if !reflect.DeepEqual(expect, plug.Metadata) {
		t.Errorf("Expected metadata %v, got %v", expect, plug.Metadata)
	}
}

func TestLoadAll(t *testing.T) {

	// Verify that empty dir loads:
	if plugs, err := LoadAll("testdata"); err != nil {
		t.Fatalf("error loading dir with no plugins: %s", err)
	} else if len(plugs) > 0 {
		t.Fatalf("expected empty dir to have 0 plugins")
	}

	basedir := "testdata/plugdir"
	plugs, err := LoadAll(basedir)
	if err != nil {
		t.Fatalf("Could not load %q: %s", basedir, err)
	}

	if l := len(plugs); l != 3 {
		t.Fatalf("expected 3 plugins, found %d", l)
	}

	if plugs[0].Metadata.Name != "downloader" {
		t.Errorf("Expected first plugin to be echo, got %q", plugs[0].Metadata.Name)
	}
	if plugs[1].Metadata.Name != "echo" {
		t.Errorf("Expected first plugin to be echo, got %q", plugs[0].Metadata.Name)
	}
	if plugs[2].Metadata.Name != "hello" {
		t.Errorf("Expected second plugin to be hello, got %q", plugs[1].Metadata.Name)
	}
}

func TestSetupEnv(t *testing.T) {
	name := "pequod"
	base := filepath.Join("testdata/helmhome/helm/plugins", name)

	s := &cli.EnvSettings{
		PluginsDirectory: "testdata/helmhome/helm/plugins",
	}

	SetupPluginEnv(s, name, base)
	for _, tt := range []struct {
		name, expect string
	}{
		{"HELM_PLUGIN_NAME", name},
		{"HELM_PLUGIN_DIR", base},
	} {
		if got := os.Getenv(tt.name); got != tt.expect {
			t.Errorf("Expected $%s=%q, got %q", tt.name, tt.expect, got)
		}
	}
}
<commit_msg>Fix tests on arm64 and ppc64le (#7500)<commit_after>/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package plugin // import "helm.sh/helm/v3/pkg/plugin"

import (
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"testing"

	"helm.sh/helm/v3/pkg/cli"
)

func checkCommand(p *Plugin, extraArgs []string, osStrCmp string, t *testing.T) {
	cmd, args, err := p.PrepareCommand(extraArgs)
	if err != nil {
		t.Errorf(err.Error())
	}
	if cmd != "echo" {
		t.Errorf("Expected echo, got %q", cmd)
	}

	if l := len(args); l != 5 {
		t.Errorf("expected 5 args, got %d", l)
	}

	expect := []string{"-n", osStrCmp, "--debug", "--foo", "bar"}
	for i := 0; i < len(args); i++ {
		if expect[i] != args[i] {
			t.Errorf("Expected arg=%q, got %q", expect[i], args[i])
		}
	}

	// Test with IgnoreFlags. This should omit --debug, --foo, bar
	p.Metadata.IgnoreFlags = true
	cmd, args, err = p.PrepareCommand(extraArgs)
	if err != nil {
		t.Errorf(err.Error())
	}
	if cmd != "echo" {
		t.Errorf("Expected echo, got %q", cmd)
	}
	if l := len(args); l != 2 {
		t.Errorf("expected 2 args, got %d", l)
	}
	expect = []string{"-n", osStrCmp}
	for i := 0; i < len(args); i++ {
		if expect[i] != args[i] {
			t.Errorf("Expected arg=%q, got %q", expect[i], args[i])
		}
	}
}

func TestPrepareCommand(t *testing.T) {
	p := &Plugin{
		Dir: "/tmp", // Unused
		Metadata: &Metadata{
			Name:    "test",
			Command: "echo -n foo",
		},
	}
	argv := []string{"--debug", "--foo", "bar"}

	checkCommand(p, argv, "foo", t)
}

func TestPlatformPrepareCommand(t *testing.T) {
	p := &Plugin{
		Dir: "/tmp", // Unused
		Metadata: &Metadata{
			Name:    "test",
			Command: "echo -n os-arch",
			PlatformCommand: []PlatformCommand{
				{OperatingSystem: "linux", Architecture: "i386", Command: "echo -n linux-i386"},
				{OperatingSystem: "linux", Architecture: "amd64", Command: "echo -n linux-amd64"},
				{OperatingSystem: "linux", Architecture: "arm64", Command: "echo -n linux-arm64"},
				{OperatingSystem: "linux", Architecture: "ppc64le", Command: "echo -n linux-ppc64le"},
				{OperatingSystem: "linux", Architecture: "s390x", Command: "echo -n linux-s390x"},
				{OperatingSystem: "windows", Architecture: "amd64", Command: "echo -n win-64"},
			},
		},
	}
	var osStrCmp string
	os := runtime.GOOS
	arch := runtime.GOARCH
	if os == "linux" && arch == "i386" {
		osStrCmp = "linux-i386"
	} else if os == "linux" && arch == "amd64" {
		osStrCmp = "linux-amd64"
	} else if os == "linux" && arch == "arm64" {
		osStrCmp = "linux-arm64"
	} else if os == "linux" && arch == "ppc64le" {
		osStrCmp = "linux-ppc64le"
	} else if os == "linux" && arch == "s390x" {
		osStrCmp = "linux-s390x"
	} else if os == "windows" && arch == "amd64" {
		osStrCmp = "win-64"
	} else {
		osStrCmp = "os-arch"
	}

	argv := []string{"--debug", "--foo", "bar"}
	checkCommand(p, argv, osStrCmp, t)
}

func TestPartialPlatformPrepareCommand(t *testing.T) {
	p := &Plugin{
		Dir: "/tmp", // Unused
		Metadata: &Metadata{
			Name:    "test",
			Command: "echo -n os-arch",
			PlatformCommand: []PlatformCommand{
				{OperatingSystem: "linux", Architecture: "i386", Command: "echo -n linux-i386"},
				{OperatingSystem: "windows", Architecture: "amd64", Command: "echo -n win-64"},
			},
		},
	}
	var osStrCmp string
	os := runtime.GOOS
	arch := runtime.GOARCH
	if os == "linux" {
		osStrCmp = "linux-i386"
	} else if os == "windows" && arch == "amd64" {
		osStrCmp = "win-64"
	} else {
		osStrCmp = "os-arch"
	}

	argv := []string{"--debug", "--foo", "bar"}
	checkCommand(p, argv, osStrCmp, t)
}

func TestNoPrepareCommand(t *testing.T) {
	p := &Plugin{
		Dir: "/tmp", // Unused
		Metadata: &Metadata{
			Name: "test",
		},
	}
	argv := []string{"--debug", "--foo", "bar"}

	_, _, err := p.PrepareCommand(argv)
	if err == nil {
		t.Errorf("Expected error to be returned")
	}
}

func TestNoMatchPrepareCommand(t *testing.T) {
	p := &Plugin{
		Dir: "/tmp", // Unused
		Metadata: &Metadata{
			Name: "test",
			PlatformCommand: []PlatformCommand{
				{OperatingSystem: "no-os", Architecture: "amd64", Command: "echo -n linux-i386"},
			},
		},
	}
	argv := []string{"--debug", "--foo", "bar"}

	if _, _, err := p.PrepareCommand(argv); err == nil {
		t.Errorf("Expected error to be returned")
	}
}

func TestLoadDir(t *testing.T) {
	dirname := "testdata/plugdir/hello"
	plug, err := LoadDir(dirname)
	if err != nil {
		t.Fatalf("error loading Hello plugin: %s", err)
	}

	if plug.Dir != dirname {
		t.Errorf("Expected dir %q, got %q", dirname, plug.Dir)
	}

	expect := &Metadata{
		Name:        "hello",
		Version:     "0.1.0",
		Usage:       "usage",
		Description: "description",
		Command:     "$HELM_PLUGIN_SELF/hello.sh",
		IgnoreFlags: true,
		Hooks: map[string]string{
			Install: "echo installing...",
		},
	}

	if !reflect.DeepEqual(expect, plug.Metadata) {
		t.Errorf("Expected plugin metadata %v, got %v", expect, plug.Metadata)
	}
}

func TestDownloader(t *testing.T) {
	dirname := "testdata/plugdir/downloader"
	plug, err := LoadDir(dirname)
	if err != nil {
		t.Fatalf("error loading Hello plugin: %s", err)
	}

	if plug.Dir != dirname {
		t.Errorf("Expected dir %q, got %q", dirname, plug.Dir)
	}

	expect := &Metadata{
		Name:        "downloader",
		Version:     "1.2.3",
		Usage:       "usage",
		Description: "download something",
		Command:     "echo Hello",
		Downloaders: []Downloaders{
			{
				Protocols: []string{"myprotocol", "myprotocols"},
				Command:   "echo Download",
			},
		},
	}

	if !reflect.DeepEqual(expect, plug.Metadata) {
		t.Errorf("Expected metadata %v, got %v", expect, plug.Metadata)
	}
}

func TestLoadAll(t *testing.T) {

	// Verify that empty dir loads:
	if plugs, err := LoadAll("testdata"); err != nil {
		t.Fatalf("error loading dir with no plugins: %s", err)
	} else if len(plugs) > 0 {
		t.Fatalf("expected empty dir to have 0 plugins")
	}

	basedir := "testdata/plugdir"
	plugs, err := LoadAll(basedir)
	if err != nil {
		t.Fatalf("Could not load %q: %s", basedir, err)
	}

	if l := len(plugs); l != 3 {
		t.Fatalf("expected 3 plugins, found %d", l)
	}

	if plugs[0].Metadata.Name != "downloader" {
		t.Errorf("Expected first plugin to be echo, got %q", plugs[0].Metadata.Name)
	}
	if plugs[1].Metadata.Name != "echo" {
		t.Errorf("Expected first plugin to be echo, got %q", plugs[0].Metadata.Name)
	}
	if plugs[2].Metadata.Name != "hello" {
		t.Errorf("Expected second plugin to be hello, got %q", plugs[1].Metadata.Name)
	}
}

func TestSetupEnv(t *testing.T) {
	name := "pequod"
	base := filepath.Join("testdata/helmhome/helm/plugins", name)

	s := &cli.EnvSettings{
		PluginsDirectory: "testdata/helmhome/helm/plugins",
	}

	SetupPluginEnv(s, name, base)
	for _, tt := range []struct {
		name, expect string
	}{
		{"HELM_PLUGIN_NAME", name},
		{"HELM_PLUGIN_DIR", base},
	} {
		if got := os.Getenv(tt.name); got != tt.expect {
			t.Errorf("Expected $%s=%q, got %q", tt.name, tt.expect, got)
		}
	}
}
<|endoftext|>


<commit_before>package provisioning

import (
	log "github.com/Sirupsen/logrus"
	"github.com/hypersleep/easyssh"
	"os/exec"
	"github.com/intelsdi-x/swan/pkg/isolation"
)

// Shell is responsible for providing the execution environment via bash or bash & ssh
// in case of remote execution. It also needed to setup given isolation
// using Isolation Manager.
type Shell struct{}

// NewShell returns a Shell instance.
func NewShell() Shell {
	return Shell{}
}

func targetIsLocal(targetHost string) bool {
	return targetHost == "local" || targetHost == "localhost" || targetHost == "127.0.0.1"
}

// Execute runs the task given in parameters
// parallel. Returned channel specifies when task has completed or failed.
func (s Shell) Execute(command string, targetHost string,
					   isolations []isolation.Isolation) (<-chan Status) {
	statusCh := make(chan Status)

	if targetIsLocal(targetHost) {
		taskPidCh := make(chan int)
		taskPid := -1

		// TODO(bplotka): We can move isolation to both ways of deploy (remote & local)
		// when we find a way to get PID from process under ssh session.
		// Do initialization of the isolation synchronously.
		for _, isolation := range isolations {
			isolation.Init(targetHost)
		}

		// Run task in shell locally.
		go func() {
			log.Debug("Starting ", command)
			cmd := exec.Command("sh", "-c", command)
			cmd.Start()

			log.Debug("Started with pid ", cmd.Process.Pid)
			// Report the process id.
			taskPidCh <- cmd.Process.Pid
			// Wait for task completion.
			cmd.Wait()
			log.Debug("Ended ", command)

			// TODO(bplotka): Fetch status code.

			output, _ := cmd.Output()
			statusCh <- Status{0, string(output)}
		}()

		// Get PID
		taskPid = <-taskPidCh

		// Perform rest of the isolation synchronously.
		for _, isolation := range isolations {
			isolation.Perform(taskPid)
		}
	} else {
		// Run task in shell remotelly via ssh.
		ssh := &easyssh.MakeConfig{
			User:   "root",
			Server: "localhost",
			Key:    "/.ssh/id_rsa",
			Port:   "22",
		}
		go func() {
			log.Debug("Starting remote ", command)

			// TODO(bplotka): Find a way to have a PID here for future isolation.
			response, err := ssh.Run(command)

			if err != nil {
				panic("Can't run remote command: " + err.Error())
			}

			log.Debug("Ended ", command)

			// TODO(bplotka): Fetch status code.
			statusCh <- Status{0, response}
		}()
	}

	return statusCh
}
<commit_msg>Modified comment.<commit_after>package provisioning

import (
	log "github.com/Sirupsen/logrus"
	"github.com/hypersleep/easyssh"
	"os/exec"
	"github.com/intelsdi-x/swan/pkg/isolation"
)

// Shell is responsible for providing the execution environment via bash or bash & ssh
// in case of remote execution. It also needed to setup given isolation
// using Isolation Manager.
type Shell struct{}

// NewShell returns a Shell instance.
func NewShell() Shell {
	return Shell{}
}

func targetIsLocal(targetHost string) bool {
	return targetHost == "local" || targetHost == "localhost" || targetHost == "127.0.0.1"
}

// Execute runs the task given in parameters.
// Returned channel specifies when task has completed or failed.
func (s Shell) Execute(command string, targetHost string,
					   isolations []isolation.Isolation) (<-chan Status) {
	statusCh := make(chan Status)

	if targetIsLocal(targetHost) {
		taskPidCh := make(chan int)
		taskPid := -1

		// TODO(bplotka): We can move isolation to both ways of deploy (remote & local)
		// when we find a way to get PID from process under ssh session.
		// Do initialization of the isolation synchronously.
		for _, isolation := range isolations {
			isolation.Init(targetHost)
		}

		// Run task in shell locally.
		go func() {
			log.Debug("Starting ", command)
			cmd := exec.Command("sh", "-c", command)
			cmd.Start()

			log.Debug("Started with pid ", cmd.Process.Pid)
			// Report the process id.
			taskPidCh <- cmd.Process.Pid
			// Wait for task completion.
			cmd.Wait()
			log.Debug("Ended ", command)

			// TODO(bplotka): Fetch status code.

			output, _ := cmd.Output()
			statusCh <- Status{0, string(output)}
		}()

		// Get PID
		taskPid = <-taskPidCh

		// Perform rest of the isolation synchronously.
		for _, isolation := range isolations {
			isolation.Perform(taskPid)
		}
	} else {
		// Run task in shell remotelly via ssh.
		ssh := &easyssh.MakeConfig{
			User:   "root",
			Server: "localhost",
			Key:    "/.ssh/id_rsa",
			Port:   "22",
		}
		go func() {
			log.Debug("Starting remote ", command)

			// TODO(bplotka): Find a way to have a PID here for future isolation.
			response, err := ssh.Run(command)

			if err != nil {
				panic("Can't run remote command: " + err.Error())
			}

			log.Debug("Ended ", command)

			// TODO(bplotka): Fetch status code.
			statusCh <- Status{0, response}
		}()
	}

	return statusCh
}
<|endoftext|>


<commit_before>package recovery

import (
	"context"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
)

type apiServerBackend struct {
	client *kubernetes.Clientset
}

// NewAPIServerBackend constructs a new backend to talk to the API server using the given
// kubeConfig.
//
// TODO(diegs): support using a service account instead of a kubeconfig.
func NewAPIServerBackend(kubeConfigPath string) (Backend, error) {
	kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeConfigPath},
		&clientcmd.ConfigOverrides{})
	config, err := kubeConfig.ClientConfig()
	if err != nil {
		return nil, err
	}
	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}
	return &apiServerBackend{
		client: client,
	}, nil
}

// read implements Backend.read().
func (b *apiServerBackend) read(context.Context) (*controlPlane, error) {
	cp := &controlPlane{}
	configMaps, err := b.client.CoreV1().ConfigMaps("kube-system").List(metav1.ListOptions{})
	if err != nil {
		return nil, err
	}
	cp.configMaps = *configMaps
	deployments, err := b.client.ExtensionsV1beta1().Deployments("kube-system").List(metav1.ListOptions{})
	if err != nil {
		return nil, err
	}
	cp.deployments = *deployments
	daemonSets, err := b.client.ExtensionsV1beta1().DaemonSets("kube-system").List(metav1.ListOptions{})
	if err != nil {
		return nil, err
	}
	cp.daemonSets = *daemonSets
	secrets, err := b.client.CoreV1().Secrets("kube-system").List(metav1.ListOptions{})
	if err != nil {
		return nil, err
	}
	cp.secrets = *secrets
	return cp, nil
}
<commit_msg>Handle AppsV1 resources inside bootkube recover<commit_after>package recovery

import (
	"context"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
)

type apiServerBackend struct {
	client *kubernetes.Clientset
}

// NewAPIServerBackend constructs a new backend to talk to the API server using the given
// kubeConfig.
//
// TODO(diegs): support using a service account instead of a kubeconfig.
func NewAPIServerBackend(kubeConfigPath string) (Backend, error) {
	kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeConfigPath},
		&clientcmd.ConfigOverrides{})
	config, err := kubeConfig.ClientConfig()
	if err != nil {
		return nil, err
	}
	client, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}
	return &apiServerBackend{
		client: client,
	}, nil
}

// read implements Backend.read().
func (b *apiServerBackend) read(context.Context) (*controlPlane, error) {
	cp := &controlPlane{}
	configMaps, err := b.client.CoreV1().ConfigMaps("kube-system").List(metav1.ListOptions{})
	if err != nil {
		return nil, err
	}
	cp.configMaps = *configMaps
	deployments, err := b.client.AppsV1().Deployments("kube-system").List(metav1.ListOptions{})
	if err != nil {
		return nil, err
	}
	cp.deployments = *deployments
	daemonSets, err := b.client.AppsV1().DaemonSets("kube-system").List(metav1.ListOptions{})
	if err != nil {
		return nil, err
	}
	cp.daemonSets = *daemonSets
	secrets, err := b.client.CoreV1().Secrets("kube-system").List(metav1.ListOptions{})
	if err != nil {
		return nil, err
	}
	cp.secrets = *secrets
	return cp, nil
}
<|endoftext|>