<commit_before>package kubernetes

import (
	"net"
	"reflect"
	"testing"
	"time"

	apiserveroptions "k8s.io/kubernetes/cmd/kube-apiserver/app/options"
	cmapp "k8s.io/kubernetes/cmd/kube-controller-manager/app/options"
	"k8s.io/kubernetes/pkg/api/unversioned"
	apiv1 "k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/pkg/apimachinery/registered"
	"k8s.io/kubernetes/pkg/apis/componentconfig"
	extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
	genericapiserveroptions "k8s.io/kubernetes/pkg/genericapiserver/options"
	kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
	"k8s.io/kubernetes/pkg/storage/storagebackend"
	utilconfig "k8s.io/kubernetes/pkg/util/config"
	"k8s.io/kubernetes/pkg/util/diff"
	scheduleroptions "k8s.io/kubernetes/plugin/cmd/kube-scheduler/app/options"

	configapi "github.com/openshift/origin/pkg/cmd/server/api"
)

func TestAPIServerDefaults(t *testing.T) {
	defaults := apiserveroptions.NewServerRunOptions()

	// This is a snapshot of the default config
	// If the default changes (new fields are added, or default values change), we want to know
	// Once we've reacted to the changes appropriately in BuildKubernetesMasterConfig(), update this expected default to match the new upstream defaults
	expectedDefaults := &apiserveroptions.ServerRunOptions{
		GenericServerRunOptions: &genericapiserveroptions.ServerRunOptions{
			AnonymousAuth:           false,
			BindAddress:             net.ParseIP("0.0.0.0"),
			CertDirectory:           "/var/run/kubernetes",
			InsecureBindAddress:     net.ParseIP("127.0.0.1"),
			InsecurePort:            8080,
			LongRunningRequestRE:    "(/|^)((watch|proxy)(/|$)|(logs?|portforward|exec|attach)/?$)",
			MaxRequestsInFlight:     400,
			SecurePort:              6443,
			EnableProfiling:         true,
			EnableGarbageCollection: true,
			EnableWatchCache:        true,
			MinRequestTimeout:       1800,
			ServiceNodePortRange:    genericapiserveroptions.DefaultServiceNodePortRange,
			RuntimeConfig:           utilconfig.ConfigurationMap{},
			StorageVersions:         registered.AllPreferredGroupVersions(),
			MasterCount:             1,
			DefaultStorageVersions:  registered.AllPreferredGroupVersions(),
			StorageConfig: storagebackend.Config{
				ServerList: nil,
				Prefix:     "/registry",
				DeserializationCacheSize: 0,
			},
			DefaultStorageMediaType:                  "application/json",
			AdmissionControl:                         "AlwaysAdmit",
			AuthorizationMode:                        "AlwaysAllow",
			DeleteCollectionWorkers:                  1,
			MasterServiceNamespace:                   "default",
			AuthorizationWebhookCacheAuthorizedTTL:   5 * time.Minute,
			AuthorizationWebhookCacheUnauthorizedTTL: 30 * time.Second,
		},
		EventTTL: 1 * time.Hour,
		KubeletConfig: kubeletclient.KubeletClientConfig{
			Port: 10250,
			PreferredAddressTypes: []string{
				string(apiv1.NodeHostName),
				string(apiv1.NodeInternalIP),
				string(apiv1.NodeExternalIP),
				string(apiv1.NodeLegacyHostIP),
			},
			EnableHttps: true,
			HTTPTimeout: time.Duration(5) * time.Second,
		},
		WebhookTokenAuthnCacheTTL: 2 * time.Minute,
	}

	if !reflect.DeepEqual(defaults, expectedDefaults) {
		t.Logf("expected defaults, actual defaults: \n%s", diff.ObjectReflectDiff(expectedDefaults, defaults))
		t.Errorf("Got different defaults than expected, adjust in BuildKubernetesMasterConfig and update expectedDefaults")
	}
}

func TestCMServerDefaults(t *testing.T) {
	defaults := cmapp.NewCMServer()

	// This is a snapshot of the default config
	// If the default changes (new fields are added, or default values change), we want to know
	// Once we've reacted to the changes appropriately in BuildKubernetesMasterConfig(), update this expected default to match the new upstream defaults
	expectedDefaults := &cmapp.CMServer{
		KubeControllerManagerConfiguration: componentconfig.KubeControllerManagerConfiguration{
			Port:                              10252, // disabled
			Address:                           "0.0.0.0",
			ConcurrentEndpointSyncs:           5,
			ConcurrentRCSyncs:                 5,
			ConcurrentRSSyncs:                 5,
			ConcurrentDaemonSetSyncs:          2,
			ConcurrentJobSyncs:                5,
			ConcurrentResourceQuotaSyncs:      5,
			ConcurrentDeploymentSyncs:         5,
			ConcurrentNamespaceSyncs:          2,
			ConcurrentSATokenSyncs:            5,
			ConcurrentServiceSyncs:            1,
			ConcurrentGCSyncs:                 20,
			LookupCacheSizeForRC:              4096,
			LookupCacheSizeForRS:              4096,
			LookupCacheSizeForDaemonSet:       1024,
			ConfigureCloudRoutes:              true,
			NodeCIDRMaskSize:                  24,
			ServiceSyncPeriod:                 unversioned.Duration{Duration: 5 * time.Minute},
			ResourceQuotaSyncPeriod:           unversioned.Duration{Duration: 5 * time.Minute},
			NamespaceSyncPeriod:               unversioned.Duration{Duration: 5 * time.Minute},
			PVClaimBinderSyncPeriod:           unversioned.Duration{Duration: 15 * time.Second},
			HorizontalPodAutoscalerSyncPeriod: unversioned.Duration{Duration: 30 * time.Second},
			DeploymentControllerSyncPeriod:    unversioned.Duration{Duration: 30 * time.Second},
			MinResyncPeriod:                   unversioned.Duration{Duration: 12 * time.Hour},
			RegisterRetryCount:                10,
			RouteReconciliationPeriod:         unversioned.Duration{Duration: 10 * time.Second},
			PodEvictionTimeout:                unversioned.Duration{Duration: 5 * time.Minute},
			NodeMonitorGracePeriod:            unversioned.Duration{Duration: 40 * time.Second},
			NodeStartupGracePeriod:            unversioned.Duration{Duration: 60 * time.Second},
			NodeMonitorPeriod:                 unversioned.Duration{Duration: 5 * time.Second},
			ClusterName:                       "kubernetes",
			TerminatedPodGCThreshold:          12500,
			VolumeConfiguration: componentconfig.VolumeConfiguration{
				EnableDynamicProvisioning:  true,
				EnableHostPathProvisioning: false,
				FlexVolumePluginDir:        "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/",
				PersistentVolumeRecyclerConfiguration: componentconfig.PersistentVolumeRecyclerConfiguration{
					MaximumRetry:             3,
					MinimumTimeoutNFS:        300,
					IncrementTimeoutNFS:      30,
					MinimumTimeoutHostPath:   60,
					IncrementTimeoutHostPath: 30,
				},
			},
			ContentType:  "application/vnd.kubernetes.protobuf",
			KubeAPIQPS:   20.0,
			KubeAPIBurst: 30,
			LeaderElection: componentconfig.LeaderElectionConfiguration{
				LeaderElect:   true,
				LeaseDuration: unversioned.Duration{Duration: 15 * time.Second},
				RenewDeadline: unversioned.Duration{Duration: 10 * time.Second},
				RetryPeriod:   unversioned.Duration{Duration: 2 * time.Second},
			},
			ClusterSigningCertFile:   "/etc/kubernetes/ca/ca.pem",
			ClusterSigningKeyFile:    "/etc/kubernetes/ca/ca.key",
			EnableGarbageCollector:   true,
			ReconcilerSyncLoopPeriod: unversioned.Duration{Duration: 5 * time.Second},
		},
	}

	if !reflect.DeepEqual(defaults, expectedDefaults) {
		t.Logf("expected defaults, actual defaults: \n%s", diff.ObjectReflectDiff(expectedDefaults, defaults))
		t.Errorf("Got different defaults than expected, adjust in BuildKubernetesMasterConfig and update expectedDefaults")
	}
}

func TestSchedulerServerDefaults(t *testing.T) {
	defaults := scheduleroptions.NewSchedulerServer()

	// This is a snapshot of the default config
	// If the default changes (new fields are added, or default values change), we want to know
	// Once we've reacted to the changes appropriately in BuildKubernetesMasterConfig(), update this expected default to match the new upstream defaults
	expectedDefaults := &scheduleroptions.SchedulerServer{
		KubeSchedulerConfiguration: componentconfig.KubeSchedulerConfiguration{
			Port:                           10251, // disabled
			Address:                        "0.0.0.0",
			AlgorithmProvider:              "DefaultProvider",
			ContentType:                    "application/vnd.kubernetes.protobuf",
			KubeAPIQPS:                     50,
			KubeAPIBurst:                   100,
			SchedulerName:                  "default-scheduler",
			HardPodAffinitySymmetricWeight: 1,
			FailureDomains:                 "kubernetes.io/hostname,failure-domain.beta.kubernetes.io/zone,failure-domain.beta.kubernetes.io/region",
			LeaderElection: componentconfig.LeaderElectionConfiguration{
				LeaderElect: true,
				LeaseDuration: unversioned.Duration{
					Duration: 15 * time.Second,
				},
				RenewDeadline: unversioned.Duration{
					Duration: 10 * time.Second,
				},
				RetryPeriod: unversioned.Duration{
					Duration: 2 * time.Second,
				},
			},
		},
	}

	if !reflect.DeepEqual(defaults, expectedDefaults) {
		t.Logf("expected defaults, actual defaults: \n%s", diff.ObjectReflectDiff(expectedDefaults, defaults))
		t.Errorf("Got different defaults than expected, adjust in BuildKubernetesMasterConfig and update expectedDefaults")
	}
}

func TestGetAPIGroupVersionOverrides(t *testing.T) {
	testcases := map[string]struct {
		DisabledVersions         map[string][]string
		ExpectedDisabledVersions []unversioned.GroupVersion
		ExpectedEnabledVersions  []unversioned.GroupVersion
	}{
		"empty": {
			DisabledVersions:         nil,
			ExpectedDisabledVersions: []unversioned.GroupVersion{},
			ExpectedEnabledVersions:  []unversioned.GroupVersion{apiv1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion},
		},
		"* -> v1": {
			DisabledVersions:         map[string][]string{"": {"*"}},
			ExpectedDisabledVersions: []unversioned.GroupVersion{apiv1.SchemeGroupVersion},
			ExpectedEnabledVersions:  []unversioned.GroupVersion{extensionsapiv1beta1.SchemeGroupVersion},
		},
		"v1": {
			DisabledVersions:         map[string][]string{"": {"v1"}},
			ExpectedDisabledVersions: []unversioned.GroupVersion{apiv1.SchemeGroupVersion},
			ExpectedEnabledVersions:  []unversioned.GroupVersion{extensionsapiv1beta1.SchemeGroupVersion},
		},
		"* -> v1beta1": {
			DisabledVersions:         map[string][]string{"extensions": {"*"}},
			ExpectedDisabledVersions: []unversioned.GroupVersion{extensionsapiv1beta1.SchemeGroupVersion},
			ExpectedEnabledVersions:  []unversioned.GroupVersion{apiv1.SchemeGroupVersion},
		},
		"extensions/v1beta1": {
			DisabledVersions:         map[string][]string{"extensions": {"v1beta1"}},
			ExpectedDisabledVersions: []unversioned.GroupVersion{extensionsapiv1beta1.SchemeGroupVersion},
			ExpectedEnabledVersions:  []unversioned.GroupVersion{apiv1.SchemeGroupVersion},
		},
	}

	for k, tc := range testcases {
		config := configapi.MasterConfig{KubernetesMasterConfig: &configapi.KubernetesMasterConfig{DisabledAPIGroupVersions: tc.DisabledVersions}}
		overrides := getAPIResourceConfig(config)

		for _, expected := range tc.ExpectedDisabledVersions {
			if overrides.AnyResourcesForVersionEnabled(expected) {
				t.Errorf("%s: Expected %v", k, expected)
			}
		}

		for _, expected := range tc.ExpectedEnabledVersions {
			if !overrides.AllResourcesForVersionEnabled(expected) {
				t.Errorf("%s: Expected %v", k, expected)
			}
		}
	}
}
<commit_msg>Add DisableAttachDetachReconcilerSync new flag to master_config_test<commit_after>package kubernetes

import (
	"net"
	"reflect"
	"testing"
	"time"

	apiserveroptions "k8s.io/kubernetes/cmd/kube-apiserver/app/options"
	cmapp "k8s.io/kubernetes/cmd/kube-controller-manager/app/options"
	"k8s.io/kubernetes/pkg/api/unversioned"
	apiv1 "k8s.io/kubernetes/pkg/api/v1"
	"k8s.io/kubernetes/pkg/apimachinery/registered"
	"k8s.io/kubernetes/pkg/apis/componentconfig"
	extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
	genericapiserveroptions "k8s.io/kubernetes/pkg/genericapiserver/options"
	kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
	"k8s.io/kubernetes/pkg/storage/storagebackend"
	utilconfig "k8s.io/kubernetes/pkg/util/config"
	"k8s.io/kubernetes/pkg/util/diff"
	scheduleroptions "k8s.io/kubernetes/plugin/cmd/kube-scheduler/app/options"

	configapi "github.com/openshift/origin/pkg/cmd/server/api"
)

func TestAPIServerDefaults(t *testing.T) {
	defaults := apiserveroptions.NewServerRunOptions()

	// This is a snapshot of the default config
	// If the default changes (new fields are added, or default values change), we want to know
	// Once we've reacted to the changes appropriately in BuildKubernetesMasterConfig(), update this expected default to match the new upstream defaults
	expectedDefaults := &apiserveroptions.ServerRunOptions{
		GenericServerRunOptions: &genericapiserveroptions.ServerRunOptions{
			AnonymousAuth:           false,
			BindAddress:             net.ParseIP("0.0.0.0"),
			CertDirectory:           "/var/run/kubernetes",
			InsecureBindAddress:     net.ParseIP("127.0.0.1"),
			InsecurePort:            8080,
			LongRunningRequestRE:    "(/|^)((watch|proxy)(/|$)|(logs?|portforward|exec|attach)/?$)",
			MaxRequestsInFlight:     400,
			SecurePort:              6443,
			EnableProfiling:         true,
			EnableGarbageCollection: true,
			EnableWatchCache:        true,
			MinRequestTimeout:       1800,
			ServiceNodePortRange:    genericapiserveroptions.DefaultServiceNodePortRange,
			RuntimeConfig:           utilconfig.ConfigurationMap{},
			StorageVersions:         registered.AllPreferredGroupVersions(),
			MasterCount:             1,
			DefaultStorageVersions:  registered.AllPreferredGroupVersions(),
			StorageConfig: storagebackend.Config{
				ServerList: nil,
				Prefix:     "/registry",
				DeserializationCacheSize: 0,
			},
			DefaultStorageMediaType:                  "application/json",
			AdmissionControl:                         "AlwaysAdmit",
			AuthorizationMode:                        "AlwaysAllow",
			DeleteCollectionWorkers:                  1,
			MasterServiceNamespace:                   "default",
			AuthorizationWebhookCacheAuthorizedTTL:   5 * time.Minute,
			AuthorizationWebhookCacheUnauthorizedTTL: 30 * time.Second,
		},
		EventTTL: 1 * time.Hour,
		KubeletConfig: kubeletclient.KubeletClientConfig{
			Port: 10250,
			PreferredAddressTypes: []string{
				string(apiv1.NodeHostName),
				string(apiv1.NodeInternalIP),
				string(apiv1.NodeExternalIP),
				string(apiv1.NodeLegacyHostIP),
			},
			EnableHttps: true,
			HTTPTimeout: time.Duration(5) * time.Second,
		},
		WebhookTokenAuthnCacheTTL: 2 * time.Minute,
	}

	if !reflect.DeepEqual(defaults, expectedDefaults) {
		t.Logf("expected defaults, actual defaults: \n%s", diff.ObjectReflectDiff(expectedDefaults, defaults))
		t.Errorf("Got different defaults than expected, adjust in BuildKubernetesMasterConfig and update expectedDefaults")
	}
}

func TestCMServerDefaults(t *testing.T) {
	defaults := cmapp.NewCMServer()

	// This is a snapshot of the default config
	// If the default changes (new fields are added, or default values change), we want to know
	// Once we've reacted to the changes appropriately in BuildKubernetesMasterConfig(), update this expected default to match the new upstream defaults
	expectedDefaults := &cmapp.CMServer{
		KubeControllerManagerConfiguration: componentconfig.KubeControllerManagerConfiguration{
			Port:                              10252, // disabled
			Address:                           "0.0.0.0",
			ConcurrentEndpointSyncs:           5,
			ConcurrentRCSyncs:                 5,
			ConcurrentRSSyncs:                 5,
			ConcurrentDaemonSetSyncs:          2,
			ConcurrentJobSyncs:                5,
			ConcurrentResourceQuotaSyncs:      5,
			ConcurrentDeploymentSyncs:         5,
			ConcurrentNamespaceSyncs:          2,
			ConcurrentSATokenSyncs:            5,
			ConcurrentServiceSyncs:            1,
			ConcurrentGCSyncs:                 20,
			LookupCacheSizeForRC:              4096,
			LookupCacheSizeForRS:              4096,
			LookupCacheSizeForDaemonSet:       1024,
			ConfigureCloudRoutes:              true,
			NodeCIDRMaskSize:                  24,
			ServiceSyncPeriod:                 unversioned.Duration{Duration: 5 * time.Minute},
			ResourceQuotaSyncPeriod:           unversioned.Duration{Duration: 5 * time.Minute},
			NamespaceSyncPeriod:               unversioned.Duration{Duration: 5 * time.Minute},
			PVClaimBinderSyncPeriod:           unversioned.Duration{Duration: 15 * time.Second},
			HorizontalPodAutoscalerSyncPeriod: unversioned.Duration{Duration: 30 * time.Second},
			DeploymentControllerSyncPeriod:    unversioned.Duration{Duration: 30 * time.Second},
			MinResyncPeriod:                   unversioned.Duration{Duration: 12 * time.Hour},
			RegisterRetryCount:                10,
			RouteReconciliationPeriod:         unversioned.Duration{Duration: 10 * time.Second},
			PodEvictionTimeout:                unversioned.Duration{Duration: 5 * time.Minute},
			NodeMonitorGracePeriod:            unversioned.Duration{Duration: 40 * time.Second},
			NodeStartupGracePeriod:            unversioned.Duration{Duration: 60 * time.Second},
			NodeMonitorPeriod:                 unversioned.Duration{Duration: 5 * time.Second},
			ClusterName:                       "kubernetes",
			TerminatedPodGCThreshold:          12500,
			VolumeConfiguration: componentconfig.VolumeConfiguration{
				EnableDynamicProvisioning:  true,
				EnableHostPathProvisioning: false,
				FlexVolumePluginDir:        "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/",
				PersistentVolumeRecyclerConfiguration: componentconfig.PersistentVolumeRecyclerConfiguration{
					MaximumRetry:             3,
					MinimumTimeoutNFS:        300,
					IncrementTimeoutNFS:      30,
					MinimumTimeoutHostPath:   60,
					IncrementTimeoutHostPath: 30,
				},
			},
			ContentType:  "application/vnd.kubernetes.protobuf",
			KubeAPIQPS:   20.0,
			KubeAPIBurst: 30,
			LeaderElection: componentconfig.LeaderElectionConfiguration{
				LeaderElect:   true,
				LeaseDuration: unversioned.Duration{Duration: 15 * time.Second},
				RenewDeadline: unversioned.Duration{Duration: 10 * time.Second},
				RetryPeriod:   unversioned.Duration{Duration: 2 * time.Second},
			},
			ClusterSigningCertFile:            "/etc/kubernetes/ca/ca.pem",
			ClusterSigningKeyFile:             "/etc/kubernetes/ca/ca.key",
			EnableGarbageCollector:            true,
			DisableAttachDetachReconcilerSync: false,
			ReconcilerSyncLoopPeriod:          unversioned.Duration{Duration: 5 * time.Second},
		},
	}

	if !reflect.DeepEqual(defaults, expectedDefaults) {
		t.Logf("expected defaults, actual defaults: \n%s", diff.ObjectReflectDiff(expectedDefaults, defaults))
		t.Errorf("Got different defaults than expected, adjust in BuildKubernetesMasterConfig and update expectedDefaults")
	}
}

func TestSchedulerServerDefaults(t *testing.T) {
	defaults := scheduleroptions.NewSchedulerServer()

	// This is a snapshot of the default config
	// If the default changes (new fields are added, or default values change), we want to know
	// Once we've reacted to the changes appropriately in BuildKubernetesMasterConfig(), update this expected default to match the new upstream defaults
	expectedDefaults := &scheduleroptions.SchedulerServer{
		KubeSchedulerConfiguration: componentconfig.KubeSchedulerConfiguration{
			Port:                           10251, // disabled
			Address:                        "0.0.0.0",
			AlgorithmProvider:              "DefaultProvider",
			ContentType:                    "application/vnd.kubernetes.protobuf",
			KubeAPIQPS:                     50,
			KubeAPIBurst:                   100,
			SchedulerName:                  "default-scheduler",
			HardPodAffinitySymmetricWeight: 1,
			FailureDomains:                 "kubernetes.io/hostname,failure-domain.beta.kubernetes.io/zone,failure-domain.beta.kubernetes.io/region",
			LeaderElection: componentconfig.LeaderElectionConfiguration{
				LeaderElect: true,
				LeaseDuration: unversioned.Duration{
					Duration: 15 * time.Second,
				},
				RenewDeadline: unversioned.Duration{
					Duration: 10 * time.Second,
				},
				RetryPeriod: unversioned.Duration{
					Duration: 2 * time.Second,
				},
			},
		},
	}

	if !reflect.DeepEqual(defaults, expectedDefaults) {
		t.Logf("expected defaults, actual defaults: \n%s", diff.ObjectReflectDiff(expectedDefaults, defaults))
		t.Errorf("Got different defaults than expected, adjust in BuildKubernetesMasterConfig and update expectedDefaults")
	}
}

func TestGetAPIGroupVersionOverrides(t *testing.T) {
	testcases := map[string]struct {
		DisabledVersions         map[string][]string
		ExpectedDisabledVersions []unversioned.GroupVersion
		ExpectedEnabledVersions  []unversioned.GroupVersion
	}{
		"empty": {
			DisabledVersions:         nil,
			ExpectedDisabledVersions: []unversioned.GroupVersion{},
			ExpectedEnabledVersions:  []unversioned.GroupVersion{apiv1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion},
		},
		"* -> v1": {
			DisabledVersions:         map[string][]string{"": {"*"}},
			ExpectedDisabledVersions: []unversioned.GroupVersion{apiv1.SchemeGroupVersion},
			ExpectedEnabledVersions:  []unversioned.GroupVersion{extensionsapiv1beta1.SchemeGroupVersion},
		},
		"v1": {
			DisabledVersions:         map[string][]string{"": {"v1"}},
			ExpectedDisabledVersions: []unversioned.GroupVersion{apiv1.SchemeGroupVersion},
			ExpectedEnabledVersions:  []unversioned.GroupVersion{extensionsapiv1beta1.SchemeGroupVersion},
		},
		"* -> v1beta1": {
			DisabledVersions:         map[string][]string{"extensions": {"*"}},
			ExpectedDisabledVersions: []unversioned.GroupVersion{extensionsapiv1beta1.SchemeGroupVersion},
			ExpectedEnabledVersions:  []unversioned.GroupVersion{apiv1.SchemeGroupVersion},
		},
		"extensions/v1beta1": {
			DisabledVersions:         map[string][]string{"extensions": {"v1beta1"}},
			ExpectedDisabledVersions: []unversioned.GroupVersion{extensionsapiv1beta1.SchemeGroupVersion},
			ExpectedEnabledVersions:  []unversioned.GroupVersion{apiv1.SchemeGroupVersion},
		},
	}

	for k, tc := range testcases {
		config := configapi.MasterConfig{KubernetesMasterConfig: &configapi.KubernetesMasterConfig{DisabledAPIGroupVersions: tc.DisabledVersions}}
		overrides := getAPIResourceConfig(config)

		for _, expected := range tc.ExpectedDisabledVersions {
			if overrides.AnyResourcesForVersionEnabled(expected) {
				t.Errorf("%s: Expected %v", k, expected)
			}
		}

		for _, expected := range tc.ExpectedEnabledVersions {
			if !overrides.AllResourcesForVersionEnabled(expected) {
				t.Errorf("%s: Expected %v", k, expected)
			}
		}
	}
}
<|endoftext|>


<commit_before>// +build !linux,!windows

/*
Copyright 2015 The Kubernetes 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 cm

import (
	"fmt"

	"k8s.io/api/core/v1"
	"k8s.io/client-go/tools/record"
	"k8s.io/kubernetes/pkg/kubelet/cadvisor"
	"k8s.io/kubernetes/pkg/util/mount"
)

type unsupportedContainerManager struct {
}

var _ ContainerManager = &unsupportedContainerManager{}

func (unsupportedContainerManager) Start(_ *v1.Node, _ ActivePodsFunc) error {
	return fmt.Errorf("Container Manager is unsupported in this build")
}

func (unsupportedContainerManager) SystemCgroupsLimit() v1.ResourceList {
	return v1.ResourceList{}
}

func (unsupportedContainerManager) GetNodeConfig() NodeConfig {
	return NodeConfig{}
}

func (unsupportedContainerManager) GetMountedSubsystems() *CgroupSubsystems {
	return &CgroupSubsystems{}
}

func (unsupportedContainerManager) GetQOSContainersInfo() QOSContainersInfo {
	return QOSContainersInfo{}
}

func (unsupportedContainerManager) UpdateQOSCgroups() error {
	return nil
}

func (cm *unsupportedContainerManager) Status() Status {
	return Status{}
}

func (cm *unsupportedContainerManager) GetNodeAllocatableReservation() v1.ResourceList {
	return nil
}

func (cm *unsupportedContainerManager) NewPodContainerManager() PodContainerManager {
	return &unsupportedPodContainerManager{}
}

func NewContainerManager(_ mount.Interface, _ cadvisor.Interface, _ NodeConfig, failSwapOn bool, recorder record.EventRecorder) (ContainerManager, error) {
	return &unsupportedContainerManager{}, nil
}
<commit_msg>Implement GetCapacity in container_manager_unsupported<commit_after>// +build !linux,!windows

/*
Copyright 2015 The Kubernetes 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 cm

import (
	"fmt"

	"k8s.io/api/core/v1"
	"k8s.io/client-go/tools/record"
	"k8s.io/kubernetes/pkg/kubelet/cadvisor"
	"k8s.io/kubernetes/pkg/util/mount"
)

type unsupportedContainerManager struct {
}

var _ ContainerManager = &unsupportedContainerManager{}

func (unsupportedContainerManager) Start(_ *v1.Node, _ ActivePodsFunc) error {
	return fmt.Errorf("Container Manager is unsupported in this build")
}

func (unsupportedContainerManager) SystemCgroupsLimit() v1.ResourceList {
	return v1.ResourceList{}
}

func (unsupportedContainerManager) GetNodeConfig() NodeConfig {
	return NodeConfig{}
}

func (unsupportedContainerManager) GetMountedSubsystems() *CgroupSubsystems {
	return &CgroupSubsystems{}
}

func (unsupportedContainerManager) GetQOSContainersInfo() QOSContainersInfo {
	return QOSContainersInfo{}
}

func (unsupportedContainerManager) UpdateQOSCgroups() error {
	return nil
}

func (cm *unsupportedContainerManager) Status() Status {
	return Status{}
}

func (cm *unsupportedContainerManager) GetNodeAllocatableReservation() v1.ResourceList {
	return nil
}

func (cm *unsupportedContainerManager) GetCapacity() v1.ResourceList {
	return nil
}

func (cm *unsupportedContainerManager) NewPodContainerManager() PodContainerManager {
	return &unsupportedPodContainerManager{}
}

func NewContainerManager(_ mount.Interface, _ cadvisor.Interface, _ NodeConfig, failSwapOn bool, recorder record.EventRecorder) (ContainerManager, error) {
	return &unsupportedContainerManager{}, nil
}
<|endoftext|>


<commit_before>// Copyright 2019 Istio 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 istio

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"

	"github.com/hashicorp/go-multierror"

	"istio.io/istio/pkg/test/cert/ca"
	"istio.io/istio/pkg/test/deployment"
	"istio.io/istio/pkg/test/env"
	"istio.io/istio/pkg/test/framework/components/environment/kube"
	"istio.io/istio/pkg/test/framework/components/istioctl"
	"istio.io/istio/pkg/test/framework/image"
	"istio.io/istio/pkg/test/framework/resource"
	"istio.io/istio/pkg/test/scopes"
)

type operatorComponent struct {
	id          resource.ID
	settings    Config
	ctx         resource.Context
	environment *kube.Environment
	// installManifest includes the yamls use to install Istio. These can be deleted on cleanup
	// The key is the cluster name
	installManifest map[string]string
}

var _ io.Closer = &operatorComponent{}
var _ Instance = &operatorComponent{}
var _ resource.Dumper = &operatorComponent{}

// ID implements resource.Instance
func (i *operatorComponent) ID() resource.ID {
	return i.id
}

func (i *operatorComponent) Settings() Config {
	return i.settings
}

func (i *operatorComponent) Close() (err error) {
	scopes.CI.Infof("=== BEGIN: Cleanup Istio [Suite=%s] ===", i.ctx.Settings().TestID)
	defer scopes.CI.Infof("=== DONE: Cleanup Istio [Suite=%s] ===", i.ctx.Settings().TestID)
	if i.settings.DeployIstio {
		for _, cluster := range i.environment.KubeClusters {
			if e := cluster.DeleteContents("", i.installManifest[cluster.Name()]); e != nil {
				err = multierror.Append(err, e)
			}
		}
	}
	return
}

func (i *operatorComponent) Dump() {
	scopes.CI.Errorf("=== Dumping Istio Deployment State...")

	for _, cluster := range i.environment.KubeClusters {
		d, err := i.ctx.CreateTmpDirectory(fmt.Sprintf("istio-state-%s", cluster.Name()))
		if err != nil {
			scopes.CI.Errorf("Unable to create directory for dumping Istio contents: %v", err)
			return
		}

		deployment.DumpPodState(d, i.settings.SystemNamespace, cluster.Accessor)
		deployment.DumpPodEvents(d, i.settings.SystemNamespace, cluster.Accessor)

		pods, err := cluster.GetPods(i.settings.SystemNamespace)
		if err != nil {
			scopes.CI.Errorf("Unable to get pods from the system namespace in cluster %s: %v", cluster.Name(), err)
			return
		}

		for _, pod := range pods {
			for _, container := range pod.Spec.Containers {
				l, err := i.environment.KubeClusters[0].Logs(pod.Namespace, pod.Name, container.Name, false /* previousLog */)
				if err != nil {
					scopes.CI.Errorf("Unable to get logs for pod/container in cluster %s: %s/%s/%s", cluster.Name(),
						pod.Namespace, pod.Name, container.Name)
					continue
				}

				fname := path.Join(d, fmt.Sprintf("%s-%s.log", pod.Name, container.Name))
				if err = ioutil.WriteFile(fname, []byte(l), os.ModePerm); err != nil {
					scopes.CI.Errorf("Unable to write logs for pod/container in cluster %s: %s/%s/%s", cluster.Name(),
						pod.Namespace, pod.Name, container.Name)
				}
			}
		}
	}
}

func deploy(ctx resource.Context, env *kube.Environment, cfg Config) (Instance, error) {
	scopes.CI.Infof("=== Istio Component Config ===")
	scopes.CI.Infof("\n%s", cfg.String())
	scopes.CI.Infof("================================")

	i := &operatorComponent{
		environment:     env,
		settings:        cfg,
		ctx:             ctx,
		installManifest: map[string]string{},
	}
	i.id = ctx.TrackResource(i)

	if !cfg.DeployIstio {
		scopes.Framework.Info("skipping deployment as specified in the config")
		return i, nil
	}

	// Top-level work dir for Istio deployment.
	workDir, err := ctx.CreateTmpDirectory("istio-deployment")
	if err != nil {
		return nil, err
	}

	// For multicluster, create and push the CA certs to all clusters to establish a shared root of trust.
	if env.IsMulticluster() {
		if err := deployCACerts(workDir, env, cfg); err != nil {
			return nil, err
		}
	}

	// Generate the istioctl config file
	iopFile := filepath.Join(workDir, "iop.yaml")
	operatorYaml := cfg.IstioOperatorConfigYAML()
	if err := ioutil.WriteFile(iopFile, []byte(operatorYaml), os.ModePerm); err != nil {
		return nil, fmt.Errorf("failed to write iop: %v", err)
	}

	// Deploy the Istio control plane(s)
	for _, cluster := range env.KubeClusters {
		if cluster.IsControlPlaneCluster() {
			if err := deployControlPlane(i, cfg, cluster, iopFile); err != nil {
				return nil, fmt.Errorf("failed deploying control plane to cluster %d: %v", cluster.Index(), err)
			}
		}
	}

	if env.IsMulticluster() {
		// For multicluster, configure direct access so each control plane can get endpoints from all
		// API servers.
		if err := configureDirectAPIServerAccess(ctx, env, cfg); err != nil {
			return nil, err
		}
	}

	// Wait for all of the control planes to be started.
	for _, cluster := range env.KubeClusters {
		if err := waitForControlPlane(i, cluster, cfg); err != nil {
			return nil, err
		}
	}

	return i, nil
}

func deployControlPlane(c *operatorComponent, cfg Config, cluster kube.Cluster, iopFile string) error {
	// Create an istioctl to configure this cluster.
	istioCtl, err := istioctl.New(c.ctx, istioctl.Config{
		Cluster: cluster,
	})
	if err != nil {
		return err
	}

	s, err := image.SettingsFromCommandLine()
	if err != nil {
		return err
	}
	defaultsIOPFile := cfg.IOPFile
	if !path.IsAbs(defaultsIOPFile) {
		defaultsIOPFile = filepath.Join(env.IstioSrc, defaultsIOPFile)
	}

	installSettings := []string{
		"-f", defaultsIOPFile,
		"-f", iopFile,
		"--set", "values.global.imagePullPolicy=" + s.PullPolicy,
	}
	// If control plane values set, assume this includes the full set of values, and .Values is
	// just for helm use case. Otherwise, include all values.
	if cfg.ControlPlaneValues == "" {
		for k, v := range cfg.Values {
			installSettings = append(installSettings, "--set", fmt.Sprintf("values.%s=%s", k, v))
		}
	}
	if c.environment.IsMulticluster() {
		// Set the clusterName for the local cluster.
		// This MUST match the clusterName in the remote secret for this cluster.
		installSettings = append(installSettings, "--set", "values.global.multiCluster.clusterName="+cluster.Name())
	}

	// Save the manifest generate output so we can later cleanup
	genCmd := []string{"manifest", "generate"}
	genCmd = append(genCmd, installSettings...)
	out, e := istioCtl.Invoke(genCmd)
	if e != nil {
		return err
	}
	c.installManifest[cluster.Name()] = out

	// Actually run the manifest apply command
	cmd := []string{
		"manifest", "apply",
		"--skip-confirmation",
		"--logtostderr",
		"--wait",
	}
	cmd = append(cmd, installSettings...)
	scopes.CI.Infof("Running istio control plane on cluster %s %v", cluster.Name(), cmd)
	if _, err := istioCtl.Invoke(cmd); err != nil {
		return fmt.Errorf("manifest apply failed: %v", err)
	}

	return nil
}

func waitForControlPlane(dumper resource.Dumper, cluster kube.Cluster, cfg Config) error {
	if !cfg.SkipWaitForValidationWebhook {
		// Wait for the validation webhook to come online before continuing.
		if _, _, err := cluster.WaitUntilServiceEndpointsAreReady(cfg.SystemNamespace, "istiod"); err != nil {
			err = fmt.Errorf("error waiting %s/%s service endpoints: %v", cfg.SystemNamespace, "istiod", err)
			scopes.CI.Info(err.Error())
			dumper.Dump()
			return err
		}

		// Wait for webhook to come online. The only reliable way to do that is to see if we can submit invalid config.
		if err := waitForValidationWebhook(cluster.Accessor, cfg); err != nil {
			dumper.Dump()
			return err
		}
	}
	return nil
}

func configureDirectAPIServerAccess(ctx resource.Context, env *kube.Environment, cfg Config) error {
	// Configure direct access for each control plane to each APIServer. This allows each control plane to
	// automatically discover endpoints in remote clusters.
	for _, cluster := range env.KubeClusters {
		// Create a secret.
		secret, err := createRemoteSecret(ctx, cluster)
		if err != nil {
			return fmt.Errorf("failed creating remote secret for cluster %s: %v", cluster.Name(), err)
		}

		// Copy this secret to all control plane clusters.
		for _, remote := range env.ControlPlaneClusters() {
			if cluster.Index() != remote.Index() {
				if _, err := remote.ApplyContents(cfg.SystemNamespace, secret); err != nil {
					return fmt.Errorf("failed applying remote secret to cluster %s: %v", remote.Name(), err)
				}
			}
		}
	}
	return nil
}

func createRemoteSecret(ctx resource.Context, cluster kube.Cluster) (string, error) {
	istioCtl, err := istioctl.New(ctx, istioctl.Config{
		Cluster: cluster,
	})
	if err != nil {
		return "", err
	}
	cmd := []string{
		"x", "create-remote-secret",
		"--name", cluster.Name(),
	}

	scopes.CI.Infof("Creating remote secret for cluster cluster %d %v", cluster.Index(), cmd)
	out, err := istioCtl.Invoke(cmd)
	if err != nil {
		return "", fmt.Errorf("create remote secret failed for cluster %d: %v", cluster.Index(), err)
	}
	return out, nil
}

func deployCACerts(workDir string, env *kube.Environment, cfg Config) error {
	certsDir := filepath.Join(workDir, "cacerts")
	if err := os.Mkdir(certsDir, 0700); err != nil {
		return err
	}

	root, err := ca.NewRoot(certsDir)
	if err != nil {
		return fmt.Errorf("failed creating the root CA: %v", err)
	}

	for _, cluster := range env.KubeClusters {
		// Create a subdir for the cluster certs.
		clusterDir := filepath.Join(certsDir, cluster.Name())
		if err := os.Mkdir(clusterDir, 0700); err != nil {
			return err
		}

		// Create the new extensions config for the CA
		caConfig, err := ca.NewIstioConfig(cfg.SystemNamespace)
		if err != nil {
			return err
		}

		// Create the certs for the cluster.
		clusterCA, err := ca.NewIntermediate(clusterDir, caConfig, root)
		if err != nil {
			return fmt.Errorf("failed creating intermediate CA for cluster %s: %v", cluster.Name(), err)
		}

		// Create the CA secret for this cluster. Istio will use these certs for its CA rather
		// than its autogenerated self-signed root.
		secret, err := clusterCA.NewIstioCASecret()
		if err != nil {
			return fmt.Errorf("failed creating intermediate CA secret for cluster %s: %v", cluster.Name(), err)
		}

		// Create the system namespace.
		if err := cluster.CreateNamespace(cfg.SystemNamespace, ""); err != nil {
			scopes.CI.Infof("failed creating namespace %s on cluster %s. This can happen when deploying "+
				"multiple control planes. Error: %v", cfg.SystemNamespace, cluster.Name(), err)
		}

		// Create the secret for the cacerts.
		if err := cluster.CreateSecret(cfg.SystemNamespace, secret); err != nil {
			scopes.CI.Infof("failed to create CA secrets on cluster %s. This can happen when deploying "+
				"multiple control planes. Error: %v", cluster.Name(), err)
		}
	}
	return nil
}
<commit_msg>Fix error handling in test framework deployment (#22517)<commit_after>// Copyright 2019 Istio 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 istio

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"

	"github.com/hashicorp/go-multierror"

	"istio.io/istio/pkg/test/cert/ca"
	"istio.io/istio/pkg/test/deployment"
	"istio.io/istio/pkg/test/env"
	"istio.io/istio/pkg/test/framework/components/environment/kube"
	"istio.io/istio/pkg/test/framework/components/istioctl"
	"istio.io/istio/pkg/test/framework/image"
	"istio.io/istio/pkg/test/framework/resource"
	"istio.io/istio/pkg/test/scopes"
)

type operatorComponent struct {
	id          resource.ID
	settings    Config
	ctx         resource.Context
	environment *kube.Environment
	// installManifest includes the yamls use to install Istio. These can be deleted on cleanup
	// The key is the cluster name
	installManifest map[string]string
}

var _ io.Closer = &operatorComponent{}
var _ Instance = &operatorComponent{}
var _ resource.Dumper = &operatorComponent{}

// ID implements resource.Instance
func (i *operatorComponent) ID() resource.ID {
	return i.id
}

func (i *operatorComponent) Settings() Config {
	return i.settings
}

func (i *operatorComponent) Close() (err error) {
	scopes.CI.Infof("=== BEGIN: Cleanup Istio [Suite=%s] ===", i.ctx.Settings().TestID)
	defer scopes.CI.Infof("=== DONE: Cleanup Istio [Suite=%s] ===", i.ctx.Settings().TestID)
	if i.settings.DeployIstio {
		for _, cluster := range i.environment.KubeClusters {
			if e := cluster.DeleteContents("", i.installManifest[cluster.Name()]); e != nil {
				err = multierror.Append(err, e)
			}
		}
	}
	return
}

func (i *operatorComponent) Dump() {
	scopes.CI.Errorf("=== Dumping Istio Deployment State...")

	for _, cluster := range i.environment.KubeClusters {
		d, err := i.ctx.CreateTmpDirectory(fmt.Sprintf("istio-state-%s", cluster.Name()))
		if err != nil {
			scopes.CI.Errorf("Unable to create directory for dumping Istio contents: %v", err)
			return
		}

		deployment.DumpPodState(d, i.settings.SystemNamespace, cluster.Accessor)
		deployment.DumpPodEvents(d, i.settings.SystemNamespace, cluster.Accessor)

		pods, err := cluster.GetPods(i.settings.SystemNamespace)
		if err != nil {
			scopes.CI.Errorf("Unable to get pods from the system namespace in cluster %s: %v", cluster.Name(), err)
			return
		}

		for _, pod := range pods {
			for _, container := range pod.Spec.Containers {
				l, err := i.environment.KubeClusters[0].Logs(pod.Namespace, pod.Name, container.Name, false /* previousLog */)
				if err != nil {
					scopes.CI.Errorf("Unable to get logs for pod/container in cluster %s: %s/%s/%s", cluster.Name(),
						pod.Namespace, pod.Name, container.Name)
					continue
				}

				fname := path.Join(d, fmt.Sprintf("%s-%s.log", pod.Name, container.Name))
				if err = ioutil.WriteFile(fname, []byte(l), os.ModePerm); err != nil {
					scopes.CI.Errorf("Unable to write logs for pod/container in cluster %s: %s/%s/%s", cluster.Name(),
						pod.Namespace, pod.Name, container.Name)
				}
			}
		}
	}
}

func deploy(ctx resource.Context, env *kube.Environment, cfg Config) (Instance, error) {
	scopes.CI.Infof("=== Istio Component Config ===")
	scopes.CI.Infof("\n%s", cfg.String())
	scopes.CI.Infof("================================")

	i := &operatorComponent{
		environment:     env,
		settings:        cfg,
		ctx:             ctx,
		installManifest: map[string]string{},
	}
	i.id = ctx.TrackResource(i)

	if !cfg.DeployIstio {
		scopes.Framework.Info("skipping deployment as specified in the config")
		return i, nil
	}

	// Top-level work dir for Istio deployment.
	workDir, err := ctx.CreateTmpDirectory("istio-deployment")
	if err != nil {
		return nil, err
	}

	// For multicluster, create and push the CA certs to all clusters to establish a shared root of trust.
	if env.IsMulticluster() {
		if err := deployCACerts(workDir, env, cfg); err != nil {
			return nil, err
		}
	}

	// Generate the istioctl config file
	iopFile := filepath.Join(workDir, "iop.yaml")
	operatorYaml := cfg.IstioOperatorConfigYAML()
	if err := ioutil.WriteFile(iopFile, []byte(operatorYaml), os.ModePerm); err != nil {
		return nil, fmt.Errorf("failed to write iop: %v", err)
	}

	// Deploy the Istio control plane(s)
	for _, cluster := range env.KubeClusters {
		if cluster.IsControlPlaneCluster() {
			if err := deployControlPlane(i, cfg, cluster, iopFile); err != nil {
				return nil, fmt.Errorf("failed deploying control plane to cluster %d: %v", cluster.Index(), err)
			}
		}
	}

	if env.IsMulticluster() {
		// For multicluster, configure direct access so each control plane can get endpoints from all
		// API servers.
		if err := configureDirectAPIServerAccess(ctx, env, cfg); err != nil {
			return nil, err
		}
	}

	// Wait for all of the control planes to be started.
	for _, cluster := range env.KubeClusters {
		if err := waitForControlPlane(i, cluster, cfg); err != nil {
			return nil, err
		}
	}

	return i, nil
}

func deployControlPlane(c *operatorComponent, cfg Config, cluster kube.Cluster, iopFile string) error {
	// Create an istioctl to configure this cluster.
	istioCtl, err := istioctl.New(c.ctx, istioctl.Config{
		Cluster: cluster,
	})
	if err != nil {
		return err
	}

	s, err := image.SettingsFromCommandLine()
	if err != nil {
		return err
	}
	defaultsIOPFile := cfg.IOPFile
	if !path.IsAbs(defaultsIOPFile) {
		defaultsIOPFile = filepath.Join(env.IstioSrc, defaultsIOPFile)
	}

	installSettings := []string{
		"-f", defaultsIOPFile,
		"-f", iopFile,
		"--set", "values.global.imagePullPolicy=" + s.PullPolicy,
	}
	// If control plane values set, assume this includes the full set of values, and .Values is
	// just for helm use case. Otherwise, include all values.
	if cfg.ControlPlaneValues == "" {
		for k, v := range cfg.Values {
			installSettings = append(installSettings, "--set", fmt.Sprintf("values.%s=%s", k, v))
		}
	}
	if c.environment.IsMulticluster() {
		// Set the clusterName for the local cluster.
		// This MUST match the clusterName in the remote secret for this cluster.
		installSettings = append(installSettings, "--set", "values.global.multiCluster.clusterName="+cluster.Name())
	}

	// Save the manifest generate output so we can later cleanup
	genCmd := []string{"manifest", "generate"}
	genCmd = append(genCmd, installSettings...)
	out, err := istioCtl.Invoke(genCmd)
	if err != nil {
		return err
	}
	c.installManifest[cluster.Name()] = out

	// Actually run the manifest apply command
	cmd := []string{
		"manifest", "apply",
		"--skip-confirmation",
		"--logtostderr",
		"--wait",
	}
	cmd = append(cmd, installSettings...)
	scopes.CI.Infof("Running istio control plane on cluster %s %v", cluster.Name(), cmd)
	if _, err := istioCtl.Invoke(cmd); err != nil {
		return fmt.Errorf("manifest apply failed: %v", err)
	}

	return nil
}

func waitForControlPlane(dumper resource.Dumper, cluster kube.Cluster, cfg Config) error {
	if !cfg.SkipWaitForValidationWebhook {
		// Wait for the validation webhook to come online before continuing.
		if _, _, err := cluster.WaitUntilServiceEndpointsAreReady(cfg.SystemNamespace, "istiod"); err != nil {
			err = fmt.Errorf("error waiting %s/%s service endpoints: %v", cfg.SystemNamespace, "istiod", err)
			scopes.CI.Info(err.Error())
			dumper.Dump()
			return err
		}

		// Wait for webhook to come online. The only reliable way to do that is to see if we can submit invalid config.
		if err := waitForValidationWebhook(cluster.Accessor, cfg); err != nil {
			dumper.Dump()
			return err
		}
	}
	return nil
}

func configureDirectAPIServerAccess(ctx resource.Context, env *kube.Environment, cfg Config) error {
	// Configure direct access for each control plane to each APIServer. This allows each control plane to
	// automatically discover endpoints in remote clusters.
	for _, cluster := range env.KubeClusters {
		// Create a secret.
		secret, err := createRemoteSecret(ctx, cluster)
		if err != nil {
			return fmt.Errorf("failed creating remote secret for cluster %s: %v", cluster.Name(), err)
		}

		// Copy this secret to all control plane clusters.
		for _, remote := range env.ControlPlaneClusters() {
			if cluster.Index() != remote.Index() {
				if _, err := remote.ApplyContents(cfg.SystemNamespace, secret); err != nil {
					return fmt.Errorf("failed applying remote secret to cluster %s: %v", remote.Name(), err)
				}
			}
		}
	}
	return nil
}

func createRemoteSecret(ctx resource.Context, cluster kube.Cluster) (string, error) {
	istioCtl, err := istioctl.New(ctx, istioctl.Config{
		Cluster: cluster,
	})
	if err != nil {
		return "", err
	}
	cmd := []string{
		"x", "create-remote-secret",
		"--name", cluster.Name(),
	}

	scopes.CI.Infof("Creating remote secret for cluster cluster %d %v", cluster.Index(), cmd)
	out, err := istioCtl.Invoke(cmd)
	if err != nil {
		return "", fmt.Errorf("create remote secret failed for cluster %d: %v", cluster.Index(), err)
	}
	return out, nil
}

func deployCACerts(workDir string, env *kube.Environment, cfg Config) error {
	certsDir := filepath.Join(workDir, "cacerts")
	if err := os.Mkdir(certsDir, 0700); err != nil {
		return err
	}

	root, err := ca.NewRoot(certsDir)
	if err != nil {
		return fmt.Errorf("failed creating the root CA: %v", err)
	}

	for _, cluster := range env.KubeClusters {
		// Create a subdir for the cluster certs.
		clusterDir := filepath.Join(certsDir, cluster.Name())
		if err := os.Mkdir(clusterDir, 0700); err != nil {
			return err
		}

		// Create the new extensions config for the CA
		caConfig, err := ca.NewIstioConfig(cfg.SystemNamespace)
		if err != nil {
			return err
		}

		// Create the certs for the cluster.
		clusterCA, err := ca.NewIntermediate(clusterDir, caConfig, root)
		if err != nil {
			return fmt.Errorf("failed creating intermediate CA for cluster %s: %v", cluster.Name(), err)
		}

		// Create the CA secret for this cluster. Istio will use these certs for its CA rather
		// than its autogenerated self-signed root.
		secret, err := clusterCA.NewIstioCASecret()
		if err != nil {
			return fmt.Errorf("failed creating intermediate CA secret for cluster %s: %v", cluster.Name(), err)
		}

		// Create the system namespace.
		if err := cluster.CreateNamespace(cfg.SystemNamespace, ""); err != nil {
			scopes.CI.Infof("failed creating namespace %s on cluster %s. This can happen when deploying "+
				"multiple control planes. Error: %v", cfg.SystemNamespace, cluster.Name(), err)
		}

		// Create the secret for the cacerts.
		if err := cluster.CreateSecret(cfg.SystemNamespace, secret); err != nil {
			scopes.CI.Infof("failed to create CA secrets on cluster %s. This can happen when deploying "+
				"multiple control planes. Error: %v", cluster.Name(), err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>//  Copyright Istio 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 namespace

import (
	"context"
	"fmt"
	"io"
	"math/rand"
	"strings"
	"sync"
	"time"

	"github.com/google/go-cmp/cmp"
	"github.com/hashicorp/go-multierror"
	kubeApiCore "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"

	"istio.io/api/label"
	"istio.io/istio/pkg/test/framework/resource"
	kube2 "istio.io/istio/pkg/test/kube"
	"istio.io/istio/pkg/test/scopes"
	"istio.io/pkg/log"
)

var (
	idctr int64
	rnd   = rand.New(rand.NewSource(time.Now().UnixNano()))
	mu    sync.Mutex
)

// kubeNamespace represents a Kubernetes namespace. It is tracked as a resource.
type kubeNamespace struct {
	id           resource.ID
	name         string
	prefix       string
	cleanupFuncs []func() error
	ctx          resource.Context
}

func (n *kubeNamespace) Dump(ctx resource.Context) {
	scopes.Framework.Errorf("=== Dumping Namespace %s State...", n.name)

	d, err := ctx.CreateTmpDirectory(n.name + "-state")
	if err != nil {
		scopes.Framework.Errorf("Unable to create directory for dumping %s contents: %v", n.name, err)
		return
	}

	kube2.DumpPods(n.ctx, d, n.name, []string{})
	kube2.DumpDeployments(n.ctx, d, n.name)
}

var (
	_ Instance          = &kubeNamespace{}
	_ io.Closer         = &kubeNamespace{}
	_ resource.Resource = &kubeNamespace{}
	_ resource.Dumper   = &kubeNamespace{}
)

func (n *kubeNamespace) Name() string {
	return n.name
}

func (n *kubeNamespace) Prefix() string {
	return n.prefix
}

func (n *kubeNamespace) Labels() (map[string]string, error) {
	perCluster := make([]map[string]string, len(n.ctx.Clusters()))
	errG := multierror.Group{}
	for i, cluster := range n.ctx.Clusters() {
		i, cluster := i, cluster
		errG.Go(func() error {
			ns, err := cluster.Kube().CoreV1().Namespaces().Get(context.TODO(), n.Name(), metav1.GetOptions{})
			if err != nil {
				return err
			}
			perCluster[i] = ns.Labels
			return nil
		})
	}
	if err := errG.Wait().ErrorOrNil(); err != nil {
		return nil, err
	}
	for i, clusterLabels := range perCluster {
		if i == 0 {
			continue
		}
		if diff := cmp.Diff(perCluster[0], clusterLabels); diff != "" {
			log.Warnf("namespace labels are different across clusters:\n%s", diff)
		}
	}
	return perCluster[0], nil
}

func (n *kubeNamespace) SetLabel(key, value string) error {
	return n.setNamespaceLabel(key, value)
}

func (n *kubeNamespace) RemoveLabel(key string) error {
	return n.removeNamespaceLabel(key)
}

func (n *kubeNamespace) ID() resource.ID {
	return n.id
}

// Close implements io.Closer
func (n *kubeNamespace) Close() (err error) {
	// Get non-nil cleanup funcs.
	var cleanupFuncs []func() error
	for _, fn := range n.cleanupFuncs {
		if fn != nil {
			cleanupFuncs = append(cleanupFuncs, fn)
		}
	}

	// Clear out the cleanup funcs and name to mark this namespace as closed.
	n.cleanupFuncs = nil
	n.name = ""

	if len(cleanupFuncs) > 0 {
		scopes.Framework.Debugf("%s deleting namespace", n.id)

		g := multierror.Group{}
		for _, cleanup := range cleanupFuncs {
			g.Go(cleanup)
		}

		err = g.Wait().ErrorOrNil()
	}

	scopes.Framework.Debugf("%s close complete (err:%v)", n.id, err)
	return
}

func claimKube(ctx resource.Context, nsConfig *Config) (Instance, error) {
	clusters := ctx.Clusters().Kube()
	n := &kubeNamespace{
		ctx:          ctx,
		prefix:       nsConfig.Prefix,
		name:         nsConfig.Prefix,
		cleanupFuncs: make([]func() error, len(clusters)),
	}

	id := ctx.TrackResource(n)
	n.id = id
	name := nsConfig.Prefix

	g := multierror.Group{}
	for i, cluster := range clusters {
		i := i
		cluster := cluster
		if !kube2.NamespaceExists(cluster.Kube(), name) {
			g.Go(func() error {
				if _, err := cluster.Kube().CoreV1().Namespaces().Create(context.TODO(), &kubeApiCore.Namespace{
					ObjectMeta: metav1.ObjectMeta{
						Name:   name,
						Labels: createNamespaceLabels(ctx, nsConfig),
					},
				}, metav1.CreateOptions{}); err != nil {
					return err
				}

				n.cleanupFuncs[i] = func() error {
					return cluster.Kube().CoreV1().Namespaces().Delete(context.TODO(), name, kube2.DeleteOptionsForeground())
				}
				return nil
			})
		}
	}

	return n, nil
}

// setNamespaceLabel labels a namespace with the given key, value pair
func (n *kubeNamespace) setNamespaceLabel(key, value string) error {
	// need to convert '/' to '~1' as per the JSON patch spec http://jsonpatch.com/#operations
	jsonPatchEscapedKey := strings.ReplaceAll(key, "/", "~1")
	name := n.name

	g := multierror.Group{}
	for _, cluster := range n.ctx.Clusters().Kube() {
		cluster := cluster
		nsLabelPatch := fmt.Sprintf(`[{"op":"replace","path":"/metadata/labels/%s","value":"%s"}]`, jsonPatchEscapedKey, value)
		g.Go(func() error {
			_, err := cluster.Kube().CoreV1().Namespaces().Patch(context.TODO(), name, types.JSONPatchType, []byte(nsLabelPatch), metav1.PatchOptions{})
			return err
		})
	}

	return g.Wait().ErrorOrNil()
}

// removeNamespaceLabel removes namespace label with the given key
func (n *kubeNamespace) removeNamespaceLabel(key string) error {
	// need to convert '/' to '~1' as per the JSON patch spec http://jsonpatch.com/#operations
	jsonPatchEscapedKey := strings.ReplaceAll(key, "/", "~1")
	name := n.name

	g := multierror.Group{}
	for _, cluster := range n.ctx.Clusters().Kube() {
		cluster := cluster
		nsLabelPatch := fmt.Sprintf(`[{"op":"remove","path":"/metadata/labels/%s"}]`, jsonPatchEscapedKey)
		g.Go(func() error {
			_, err := cluster.Kube().CoreV1().Namespaces().Patch(context.TODO(), name, types.JSONPatchType, []byte(nsLabelPatch), metav1.PatchOptions{})
			return err
		})
	}

	return g.Wait().ErrorOrNil()
}

// NewNamespace allocates a new testing namespace.
func newKube(ctx resource.Context, nsConfig *Config) (Instance, error) {
	mu.Lock()
	idctr++
	nsid := idctr
	r := rnd.Intn(99999)
	mu.Unlock()

	clusters := ctx.Clusters().Kube()
	name := fmt.Sprintf("%s-%d-%d", nsConfig.Prefix, nsid, r)
	n := &kubeNamespace{
		name:         name,
		prefix:       nsConfig.Prefix,
		ctx:          ctx,
		cleanupFuncs: make([]func() error, len(clusters)),
	}
	id := ctx.TrackResource(n)
	n.id = id

	s := ctx.Settings()
	g := multierror.Group{}
	for i, cluster := range clusters {
		i := i
		cluster := cluster

		g.Go(func() error {
			if _, err := cluster.Kube().CoreV1().Namespaces().Create(context.TODO(), &kubeApiCore.Namespace{
				ObjectMeta: metav1.ObjectMeta{
					Name:   name,
					Labels: createNamespaceLabels(ctx, nsConfig),
				},
			}, metav1.CreateOptions{}); err != nil {
				return err
			}

			n.cleanupFuncs[i] = func() error {
				return cluster.Kube().CoreV1().Namespaces().Delete(context.TODO(), name, kube2.DeleteOptionsForeground())
			}

			if s.Image.PullSecret != "" {
				if err := cluster.ApplyYAMLFiles(n.name, s.Image.PullSecret); err != nil {
					return err
				}
			}
			return nil
		})
	}

	if err := g.Wait().ErrorOrNil(); err != nil {
		return nil, err
	}

	return n, nil
}

// createNamespaceLabels will take a namespace config and generate the proper k8s labels
func createNamespaceLabels(ctx resource.Context, cfg *Config) map[string]string {
	l := make(map[string]string)
	l["istio-testing"] = "istio-test"
	if cfg.Inject {
		// do not add namespace labels when running compatibility tests since
		// this disables the necessary object selectors
		if !ctx.Settings().Compatibility {
			if cfg.Revision != "" {
				l[label.IoIstioRev.Name] = cfg.Revision
			} else {
				l["istio-injection"] = "enabled"
			}
		}
	} else {
		// if we're running compatibility tests, disable injection in the namespace
		// explicitly so that object selectors are ignored
		if ctx.Settings().Compatibility {
			l["istio-injection"] = "disabled"
		}
	}

	// bring over supplied labels
	for k, v := range cfg.Labels {
		l[k] = v
	}
	return l
}
<commit_msg>[tf] Consistent namespace creation (#38969)<commit_after>//  Copyright Istio 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 namespace

import (
	"context"
	"fmt"
	"io"
	"math/rand"
	"strings"
	"sync"
	"time"

	"github.com/google/go-cmp/cmp"
	"github.com/hashicorp/go-multierror"
	kubeApiCore "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"

	"istio.io/api/label"
	"istio.io/istio/pkg/test/framework/components/cluster"
	"istio.io/istio/pkg/test/framework/resource"
	kube2 "istio.io/istio/pkg/test/kube"
	"istio.io/istio/pkg/test/scopes"
	"istio.io/pkg/log"
)

var (
	idctr int64
	rnd   = rand.New(rand.NewSource(time.Now().UnixNano()))
	mu    sync.Mutex
)

// kubeNamespace represents a Kubernetes namespace. It is tracked as a resource.
type kubeNamespace struct {
	ctx          resource.Context
	id           resource.ID
	name         string
	prefix       string
	cleanupMutex sync.Mutex
	cleanupFuncs []func() error
}

func (n *kubeNamespace) Dump(ctx resource.Context) {
	scopes.Framework.Errorf("=== Dumping Namespace %s State...", n.name)

	d, err := ctx.CreateTmpDirectory(n.name + "-state")
	if err != nil {
		scopes.Framework.Errorf("Unable to create directory for dumping %s contents: %v", n.name, err)
		return
	}

	kube2.DumpPods(n.ctx, d, n.name, []string{})
	kube2.DumpDeployments(n.ctx, d, n.name)
}

var (
	_ Instance          = &kubeNamespace{}
	_ io.Closer         = &kubeNamespace{}
	_ resource.Resource = &kubeNamespace{}
	_ resource.Dumper   = &kubeNamespace{}
)

func (n *kubeNamespace) Name() string {
	return n.name
}

func (n *kubeNamespace) Prefix() string {
	return n.prefix
}

func (n *kubeNamespace) Labels() (map[string]string, error) {
	perCluster := make([]map[string]string, len(n.ctx.Clusters()))
	if err := n.forEachCluster(func(i int, c cluster.Cluster) error {
		ns, err := c.Kube().CoreV1().Namespaces().Get(context.TODO(), n.Name(), metav1.GetOptions{})
		if err != nil {
			return err
		}
		perCluster[i] = ns.Labels
		return nil
	}); err != nil {
		return nil, err
	}
	for i, clusterLabels := range perCluster {
		if i == 0 {
			continue
		}
		if diff := cmp.Diff(perCluster[0], clusterLabels); diff != "" {
			log.Warnf("namespace labels are different across clusters:\n%s", diff)
		}
	}
	return perCluster[0], nil
}

func (n *kubeNamespace) SetLabel(key, value string) error {
	return n.setNamespaceLabel(key, value)
}

func (n *kubeNamespace) RemoveLabel(key string) error {
	return n.removeNamespaceLabel(key)
}

func (n *kubeNamespace) ID() resource.ID {
	return n.id
}

func (n *kubeNamespace) Close() error {
	// Get the cleanup funcs and clear the array to prevent us from cleaning up multiple times.
	n.cleanupMutex.Lock()
	cleanupFuncs := n.cleanupFuncs
	n.cleanupFuncs = nil
	n.cleanupMutex.Unlock()

	// Perform the cleanup across all clusters concurrently.
	var err error
	if len(cleanupFuncs) > 0 {
		scopes.Framework.Debugf("%s deleting namespace", n.id)

		g := multierror.Group{}
		for _, cleanup := range cleanupFuncs {
			g.Go(cleanup)
		}

		err = g.Wait().ErrorOrNil()
	}

	scopes.Framework.Debugf("%s close complete (err:%v)", n.id, err)
	return err
}

func claimKube(ctx resource.Context, cfg *Config) (Instance, error) {
	name := cfg.Prefix
	n := &kubeNamespace{
		ctx:    ctx,
		prefix: name,
		name:   name,
	}

	id := ctx.TrackResource(n)
	n.id = id

	if err := n.forEachCluster(func(_ int, c cluster.Cluster) error {
		if !kube2.NamespaceExists(c.Kube(), name) {
			return n.createInCluster(c, cfg)
		}
		return nil
	}); err != nil {
		return nil, err
	}

	return n, nil
}

// setNamespaceLabel labels a namespace with the given key, value pair
func (n *kubeNamespace) setNamespaceLabel(key, value string) error {
	// need to convert '/' to '~1' as per the JSON patch spec http://jsonpatch.com/#operations
	jsonPatchEscapedKey := strings.ReplaceAll(key, "/", "~1")
	nsLabelPatch := fmt.Sprintf(`[{"op":"replace","path":"/metadata/labels/%s","value":"%s"}]`, jsonPatchEscapedKey, value)

	return n.forEachCluster(func(_ int, c cluster.Cluster) error {
		_, err := c.Kube().CoreV1().Namespaces().Patch(context.TODO(), n.name, types.JSONPatchType, []byte(nsLabelPatch), metav1.PatchOptions{})
		return err
	})
}

// removeNamespaceLabel removes namespace label with the given key
func (n *kubeNamespace) removeNamespaceLabel(key string) error {
	// need to convert '/' to '~1' as per the JSON patch spec http://jsonpatch.com/#operations
	jsonPatchEscapedKey := strings.ReplaceAll(key, "/", "~1")
	nsLabelPatch := fmt.Sprintf(`[{"op":"remove","path":"/metadata/labels/%s"}]`, jsonPatchEscapedKey)
	name := n.name

	return n.forEachCluster(func(_ int, c cluster.Cluster) error {
		_, err := c.Kube().CoreV1().Namespaces().Patch(context.TODO(), name, types.JSONPatchType, []byte(nsLabelPatch), metav1.PatchOptions{})
		return err
	})
}

// NewNamespace allocates a new testing namespace.
func newKube(ctx resource.Context, cfg *Config) (Instance, error) {
	mu.Lock()
	idctr++
	nsid := idctr
	r := rnd.Intn(99999)
	mu.Unlock()

	name := fmt.Sprintf("%s-%d-%d", cfg.Prefix, nsid, r)
	n := &kubeNamespace{
		name:   name,
		prefix: cfg.Prefix,
		ctx:    ctx,
	}
	id := ctx.TrackResource(n)
	n.id = id

	if err := n.forEachCluster(func(_ int, c cluster.Cluster) error {
		return n.createInCluster(c, cfg)
	}); err != nil {
		return nil, err
	}

	return n, nil
}

func (n *kubeNamespace) createInCluster(c cluster.Cluster, cfg *Config) error {
	if _, err := c.Kube().CoreV1().Namespaces().Create(context.TODO(), &kubeApiCore.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name:   n.name,
			Labels: createNamespaceLabels(n.ctx, cfg),
		},
	}, metav1.CreateOptions{}); err != nil {
		return err
	}

	n.addCleanup(func() error {
		return c.Kube().CoreV1().Namespaces().Delete(context.TODO(), n.name, kube2.DeleteOptionsForeground())
	})

	s := n.ctx.Settings()
	if s.Image.PullSecret != "" {
		if err := c.ApplyYAMLFiles(n.name, s.Image.PullSecret); err != nil {
			return err
		}
	}
	return nil
}

func (n *kubeNamespace) forEachCluster(fn func(i int, c cluster.Cluster) error) error {
	errG := multierror.Group{}
	for i, c := range n.ctx.Clusters().Kube() {
		i, c := i, c
		errG.Go(func() error {
			return fn(i, c)
		})
	}
	return errG.Wait().ErrorOrNil()
}

func (n *kubeNamespace) addCleanup(fn func() error) {
	n.cleanupMutex.Lock()
	defer n.cleanupMutex.Unlock()
	n.cleanupFuncs = append(n.cleanupFuncs, fn)
}

// createNamespaceLabels will take a namespace config and generate the proper k8s labels
func createNamespaceLabels(ctx resource.Context, cfg *Config) map[string]string {
	l := make(map[string]string)
	l["istio-testing"] = "istio-test"
	if cfg.Inject {
		// do not add namespace labels when running compatibility tests since
		// this disables the necessary object selectors
		if !ctx.Settings().Compatibility {
			if cfg.Revision != "" {
				l[label.IoIstioRev.Name] = cfg.Revision
			} else {
				l["istio-injection"] = "enabled"
			}
		}
	} else {
		// if we're running compatibility tests, disable injection in the namespace
		// explicitly so that object selectors are ignored
		if ctx.Settings().Compatibility {
			l["istio-injection"] = "disabled"
		}
	}

	// bring over supplied labels
	for k, v := range cfg.Labels {
		l[k] = v
	}
	return l
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2018 Red Hat, Inc.
 *
 */

package dhcp

import (
	"bytes"
	"fmt"
	"net"
	"regexp"
	"strings"
	"time"

	dhcp "github.com/krolaw/dhcp4"
	dhcpConn "github.com/krolaw/dhcp4/conn"
	"github.com/vishvananda/netlink"

	"kubevirt.io/kubevirt/pkg/log"
)

const (
	infiniteLease             = 999 * 24 * time.Hour
	errorSearchDomainNotValid = "Search domain is not valid"
	errorSearchDomainTooLong  = "Search domains length exceeded allowable size"
)

func SingleClientDHCPServer(
	clientMAC net.HardwareAddr,
	clientIP net.IP,
	clientMask net.IPMask,
	serverIface string,
	serverIP net.IP,
	routerIP net.IP,
	dnsIPs [][]byte,
	routes *[]netlink.Route,
	searchDomains []string) error {

	log.Log.Info("Starting SingleClientDHCPServer")

	netRoutes := FormClasslessRoutes(routes, routerIP)

	searchDomainBytes, err := convertSearchDomainsToBytes(searchDomains)
	if err != nil {
		return err
	}

	handler := &DHCPHandler{
		clientIP:      clientIP,
		clientMAC:     clientMAC,
		serverIP:      serverIP.To4(),
		leaseDuration: infiniteLease,
		options: dhcp.Options{
			dhcp.OptionSubnetMask:           []byte(clientMask),
			dhcp.OptionRouter:               []byte(routerIP),
			dhcp.OptionDomainNameServer:     bytes.Join(dnsIPs, nil),
			dhcp.OptionClasslessRouteFormat: netRoutes,
			dhcp.OptionDomainSearch:         searchDomainBytes,
		},
	}

	l, err := dhcpConn.NewUDP4BoundListener(serverIface, ":67")
	if err != nil {
		return err
	}
	defer l.Close()
	err = dhcp.Serve(l, handler)
	if err != nil {
		return err
	}
	return nil
}

type DHCPHandler struct {
	serverIP      net.IP
	clientIP      net.IP
	clientMAC     net.HardwareAddr
	leaseDuration time.Duration
	options       dhcp.Options
}

func (h *DHCPHandler) ServeDHCP(p dhcp.Packet, msgType dhcp.MessageType, options dhcp.Options) (d dhcp.Packet) {
	log.Log.Debug("Serving a new request")
	if mac := p.CHAddr(); !bytes.Equal(mac, h.clientMAC) {
		log.Log.Debug("The request is not from our client")
		return nil // Is not our client
	}

	switch msgType {

	case dhcp.Discover:
		log.Log.Debug("The request has message type DISCOVER")
		return dhcp.ReplyPacket(p, dhcp.Offer, h.serverIP, h.clientIP, h.leaseDuration,
			h.options.SelectOrderOrAll(options[dhcp.OptionParameterRequestList]))

	case dhcp.Request:
		log.Log.Debug("The request has message type REQUEST")
		return dhcp.ReplyPacket(p, dhcp.ACK, h.serverIP, h.clientIP, h.leaseDuration,
			h.options.SelectOrderOrAll(options[dhcp.OptionParameterRequestList]))

	default:
		log.Log.Debug("The request has unhandled message type")
		return nil // Ignored message type

	}
}

func FormClasslessRoutes(routes *[]netlink.Route, routerIP net.IP) (formattedRoutes []byte) {
	// See RFC4332 for additional information
	// (https://tools.ietf.org/html/rfc3442)
	// For example:
	// 		routes:
	//				10.0.0.0/8 ,  gateway: 10.1.2.3
	//              192.168.1/24, gateway: 192.168.2.3
	//		would result in the following structure:
	//      []byte{8, 10, 10, 1, 2, 3, 24, 192, 168, 1, 192, 168, 2, 3}

	for _, route := range *routes {
		if route.Dst == nil {
			continue
		}
		ip := route.Dst.IP.To4()
		width, _ := route.Dst.Mask.Size()
		octets := (width-1)/8 + 1
		newRoute := append([]byte{byte(width)}, ip[0:octets]...)
		gateway := route.Gw
		if gateway == nil {
			gateway = routerIP
		}
		newRoute = append(newRoute, gateway.To4()...)
		formattedRoutes = append(formattedRoutes, newRoute...)
	}
	return
}

func convertSearchDomainsToBytes(searchDomainStrings []string) ([]byte, error) {
	/*
        https://tools.ietf.org/html/rfc3397
        https://tools.ietf.org/html/rfc1035

        Option for search domain string is covered by RFC3397, option contains
        RFC1035 domain data.

        Convert domain strings to a DNS RFC1035 section 3.1 compatible byte slice.
        This is basically just splitting the domain on dot and prepending each
        substring with a byte that indicates its length. Then we join and null terminate.

        "example.com" becomes:
        []byte{7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0}

        Note that there is a compression scheme described in section 4.1.4 where pointers
        can be used to avoid duplication. This is optional for servers, and resolv.conf
        limits max search domain length anyway, so we can skip compression.
	*/
	var searchDomainBytes []byte
	for _, domain := range searchDomainStrings {
		if isValidSearchDomain(domain) {
			labels := strings.Split(domain, ".")
			for _, label := range labels {
				searchDomainBytes = append(searchDomainBytes, byte(len(label)))
				searchDomainBytes = append(searchDomainBytes, []byte(label)...)
			}
			searchDomainBytes = append(searchDomainBytes, 0)
		} else {
			return searchDomainBytes, fmt.Errorf("%s: '%s'", errorSearchDomainNotValid, domain)
		}
	}

	// ensure we haven't gone past length limit of DHCP option data
	if len(searchDomainBytes) > 255 {
		return searchDomainBytes, fmt.Errorf("%s: was %d long", errorSearchDomainTooLong, len(searchDomainBytes))
	}

	return searchDomainBytes, nil
}

func isValidSearchDomain(domain string) bool {
	if len(domain) > 253 {
		return false
	}
	// simple domain validation regex. Note this requires that unicode domains be presented in their ASCII format
	re := regexp.MustCompile(`^(?:[_a-z0-9](?:[_a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?)?$`)
	return re.MatchString(domain)
}
<commit_msg>Commit from 'make generate'<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2018 Red Hat, Inc.
 *
 */

package dhcp

import (
	"bytes"
	"fmt"
	"net"
	"regexp"
	"strings"
	"time"

	dhcp "github.com/krolaw/dhcp4"
	dhcpConn "github.com/krolaw/dhcp4/conn"
	"github.com/vishvananda/netlink"

	"kubevirt.io/kubevirt/pkg/log"
)

const (
	infiniteLease             = 999 * 24 * time.Hour
	errorSearchDomainNotValid = "Search domain is not valid"
	errorSearchDomainTooLong  = "Search domains length exceeded allowable size"
)

func SingleClientDHCPServer(
	clientMAC net.HardwareAddr,
	clientIP net.IP,
	clientMask net.IPMask,
	serverIface string,
	serverIP net.IP,
	routerIP net.IP,
	dnsIPs [][]byte,
	routes *[]netlink.Route,
	searchDomains []string) error {

	log.Log.Info("Starting SingleClientDHCPServer")

	netRoutes := FormClasslessRoutes(routes, routerIP)

	searchDomainBytes, err := convertSearchDomainsToBytes(searchDomains)
	if err != nil {
		return err
	}

	handler := &DHCPHandler{
		clientIP:      clientIP,
		clientMAC:     clientMAC,
		serverIP:      serverIP.To4(),
		leaseDuration: infiniteLease,
		options: dhcp.Options{
			dhcp.OptionSubnetMask:           []byte(clientMask),
			dhcp.OptionRouter:               []byte(routerIP),
			dhcp.OptionDomainNameServer:     bytes.Join(dnsIPs, nil),
			dhcp.OptionClasslessRouteFormat: netRoutes,
			dhcp.OptionDomainSearch:         searchDomainBytes,
		},
	}

	l, err := dhcpConn.NewUDP4BoundListener(serverIface, ":67")
	if err != nil {
		return err
	}
	defer l.Close()
	err = dhcp.Serve(l, handler)
	if err != nil {
		return err
	}
	return nil
}

type DHCPHandler struct {
	serverIP      net.IP
	clientIP      net.IP
	clientMAC     net.HardwareAddr
	leaseDuration time.Duration
	options       dhcp.Options
}

func (h *DHCPHandler) ServeDHCP(p dhcp.Packet, msgType dhcp.MessageType, options dhcp.Options) (d dhcp.Packet) {
	log.Log.Debug("Serving a new request")
	if mac := p.CHAddr(); !bytes.Equal(mac, h.clientMAC) {
		log.Log.Debug("The request is not from our client")
		return nil // Is not our client
	}

	switch msgType {

	case dhcp.Discover:
		log.Log.Debug("The request has message type DISCOVER")
		return dhcp.ReplyPacket(p, dhcp.Offer, h.serverIP, h.clientIP, h.leaseDuration,
			h.options.SelectOrderOrAll(options[dhcp.OptionParameterRequestList]))

	case dhcp.Request:
		log.Log.Debug("The request has message type REQUEST")
		return dhcp.ReplyPacket(p, dhcp.ACK, h.serverIP, h.clientIP, h.leaseDuration,
			h.options.SelectOrderOrAll(options[dhcp.OptionParameterRequestList]))

	default:
		log.Log.Debug("The request has unhandled message type")
		return nil // Ignored message type

	}
}

func FormClasslessRoutes(routes *[]netlink.Route, routerIP net.IP) (formattedRoutes []byte) {
	// See RFC4332 for additional information
	// (https://tools.ietf.org/html/rfc3442)
	// For example:
	// 		routes:
	//				10.0.0.0/8 ,  gateway: 10.1.2.3
	//              192.168.1/24, gateway: 192.168.2.3
	//		would result in the following structure:
	//      []byte{8, 10, 10, 1, 2, 3, 24, 192, 168, 1, 192, 168, 2, 3}

	for _, route := range *routes {
		if route.Dst == nil {
			continue
		}
		ip := route.Dst.IP.To4()
		width, _ := route.Dst.Mask.Size()
		octets := (width-1)/8 + 1
		newRoute := append([]byte{byte(width)}, ip[0:octets]...)
		gateway := route.Gw
		if gateway == nil {
			gateway = routerIP
		}
		newRoute = append(newRoute, gateway.To4()...)
		formattedRoutes = append(formattedRoutes, newRoute...)
	}
	return
}

func convertSearchDomainsToBytes(searchDomainStrings []string) ([]byte, error) {
	/*
	   https://tools.ietf.org/html/rfc3397
	   https://tools.ietf.org/html/rfc1035

	   Option for search domain string is covered by RFC3397, option contains
	   RFC1035 domain data.

	   Convert domain strings to a DNS RFC1035 section 3.1 compatible byte slice.
	   This is basically just splitting the domain on dot and prepending each
	   substring with a byte that indicates its length. Then we join and null terminate.

	   "example.com" becomes:
	   []byte{7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0}

	   Note that there is a compression scheme described in section 4.1.4 where pointers
	   can be used to avoid duplication. This is optional for servers, and resolv.conf
	   limits max search domain length anyway, so we can skip compression.
	*/
	var searchDomainBytes []byte
	for _, domain := range searchDomainStrings {
		if isValidSearchDomain(domain) {
			labels := strings.Split(domain, ".")
			for _, label := range labels {
				searchDomainBytes = append(searchDomainBytes, byte(len(label)))
				searchDomainBytes = append(searchDomainBytes, []byte(label)...)
			}
			searchDomainBytes = append(searchDomainBytes, 0)
		} else {
			return searchDomainBytes, fmt.Errorf("%s: '%s'", errorSearchDomainNotValid, domain)
		}
	}

	// ensure we haven't gone past length limit of DHCP option data
	if len(searchDomainBytes) > 255 {
		return searchDomainBytes, fmt.Errorf("%s: was %d long", errorSearchDomainTooLong, len(searchDomainBytes))
	}

	return searchDomainBytes, nil
}

func isValidSearchDomain(domain string) bool {
	if len(domain) > 253 {
		return false
	}
	// simple domain validation regex. Note this requires that unicode domains be presented in their ASCII format
	re := regexp.MustCompile(`^(?:[_a-z0-9](?:[_a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?)?$`)
	return re.MatchString(domain)
}
<|endoftext|>


<commit_before>package types

import (
	"encoding/json"
	"strconv"

	"github.com/asuleymanov/golos-go/encoding/transaction"
)

type ContentMetadata map[string]interface{}

func (op *ContentMetadata) UnmarshalJSON(p []byte) error {
	str, _ := strconv.Unquote(string(p))

	if err := json.Unmarshal([]byte(str), *op); err != nil {
		return err
	}

	return nil
}

func (op *ContentMetadata) MarshalJSON() ([]byte, error) {
	ans, err := json.Marshal(*op)
	if err != nil {
		return []byte{}, err
	}
	return []byte(strconv.Quote(string(ans))), nil
}

func (op *ContentMetadata) MarshalTransaction(encoder *transaction.Encoder) error {
	ans, err := json.Marshal(*op)
	if err != nil {
		return err
	}

	str, err := strconv.Unquote(string(ans))
	if err != nil {
		return err
	}

	enc := transaction.NewRollingEncoder(encoder)
	enc.EncodeString(str)
	return enc.Err()
}
<commit_msg>Удаление лишнего Unquote.<commit_after>package types

import (
	"encoding/json"
	"strconv"

	"github.com/asuleymanov/golos-go/encoding/transaction"
)

type ContentMetadata map[string]interface{}

func (op *ContentMetadata) UnmarshalJSON(p []byte) error {
	str, _ := strconv.Unquote(string(p))

	if err := json.Unmarshal([]byte(str), *op); err != nil {
		return err
	}

	return nil
}

func (op *ContentMetadata) MarshalJSON() ([]byte, error) {
	ans, err := json.Marshal(*op)
	if err != nil {
		return []byte{}, err
	}
	return []byte(strconv.Quote(string(ans))), nil
}

func (op *ContentMetadata) MarshalTransaction(encoder *transaction.Encoder) error {
	ans, err := json.Marshal(*op)
	if err != nil {
		return err
	}

	enc := transaction.NewRollingEncoder(encoder)
	enc.EncodeString(string(ans))
	return enc.Err()
}
<|endoftext|>


<commit_before>package web_test

import (
	"testing"
	"time"

	"github.com/intervention-engine/ie"
	"github.com/intervention-engine/ie/testutil"
	"github.com/stretchr/testify/suite"
)

type careTeamSuite struct {
	testutil.WebSuite
	DB map[string]ie.CareTeam
}

func TestCareTeamHandlersSuite(t *testing.T) {
	m := make(map[string]ie.CareTeam)
	suite.Run(t, &careTeamSuite{DB: m})
}

func (suite *careTeamSuite) SetupSuite() {
	// api := suite.LoadGin()
	// api.GET("/care_teams", web.Adapt(web.ListAllCareTeams, suite.withTestService()))
	// api.POST("/care_teams", web.Adapt(web.CreateCareTeam, suite.withTestService()))
	// api.GET("/care_teams/:id", web.Adapt(web.GetCareTeam, suite.withTestService()))
	// api.PUT("/care_teams/:id", web.Adapt(web.UpdateCareTeam, suite.withTestService()))
	// api.DELETE("/care_teams/:id", web.Adapt(web.DeleteCareTeam, suite.withTestService()))
}

func (suite *careTeamSuite) SetupTest() {
	for _, team := range CareTeamsDB {
		suite.DB[team.ID] = team
	}
}

//
// // TODO: Should make some permanent times so we can test that, too
// // When there are care teams, should return a slice of care teams with 200 OK
// func (suite *careTeamSuite) TestAllCareTeamsFound() {
// 	w := suite.AssertGetRequest("/api/care_teams", http.StatusOK)
//
// 	var body = make(map[string][]ie.CareTeam)
// 	json.NewDecoder(w.Body).Decode(&body)
// 	results, ok := body["care_teams"]
// 	if !ok {
// 		suite.T().Errorf("body did not contain an element named care_teams | body: %#+v\n", w.Body.String())
// 		return
// 	}
// 	for _, team := range CareTeamsDB {
// 		result := findCareTeam(team.ID, results)
//
// 		suite.Assert().NotNil(result, "body did not contain team: %s with ID: %s\n", team.Name, team.ID)
// 		suite.Assert().Equal(team.Leader, result.Leader, "team: %s did not contain correct leader | want: %s got: %s", team.Name, team.Leader, result.Leader)
// 		suite.Assert().Equal(team.Name, result.Name, "team: %s did not contain correct name | want: %s got: %s", team.Name, team.Name, result.Name)
// 	}
// }
//
// // If a care team with that (correct) id does not exist in the database, should
// // return 404 Not Found
// func (suite *careTeamSuite) TestGetCareTeamNotFound() {
// 	id := CareTeamsDB[0].ID
// 	delete(suite.DB, id)
// 	suite.AssertGetRequest("/api/care_teams/"+id, http.StatusNotFound)
// }
//
// // When given an incorrectly formatted id, should return 400 Bad Request
// func (suite *careTeamSuite) TestGetCareTeamWithBadId() {
// 	suite.AssertGetRequest("/api/care_teams/sdf", http.StatusBadRequest)
// }
//
// // If a care team with that id exists, should return the care team and 200 OK
// func (suite *careTeamSuite) TestGetCareTeamFound() {
// 	team := CareTeamsDB[0]
// 	w := suite.AssertGetRequest("/api/care_teams/58c314acb367c1ff54d19e9e", http.StatusOK)
// 	var body = make(map[string]ie.CareTeam)
// 	json.NewDecoder(w.Body).Decode(&body)
// 	result, ok := body["care_team"]
// 	if !ok {
// 		suite.T().Errorf("body did not contain an element named care_team | body: %#+v\n", w.Body.String())
// 		return
// 	}
// 	suite.Assert().Equal(team.Leader, result.Leader, "team: %s did not contain correct leader | want: %s got: %s", team.Name, team.Leader, result.Leader)
// 	suite.Assert().Equal(team.Name, result.Name, "team: %s did not contain correct name | want: %s got: %s", team.Name, team.Name, result.Name)
// }
//
// // When called with no form data, should return 400 Bad Request
// func TestCreateCareTeamWithNoFormData(t *testing.T) {
//
// }
//
// // When called with correct form data, should return the created object in
// // properly formatted JSON
// func TestCreateCareTeamReturnsProperJSONOnSuccess(t *testing.T) {
//
// }
//
// // Mock Services
//
// func (suite *careTeamSuite) CareTeam(id string) (*ie.CareTeam, error) {
// 	if !bson.IsObjectIdHex(id) {
// 		return nil, errors.New("bad id")
// 	}
// 	c, ok := suite.DB[id]
// 	if !ok {
// 		return nil, errors.New("not found")
// 	}
// 	return &c, nil
// }
//
// func (suite *careTeamSuite) CareTeams() ([]ie.CareTeam, error) {
// 	var cc []ie.CareTeam
// 	for _, team := range suite.DB {
// 		cc = append(cc, team)
// 	}
// 	return cc, nil
// }
//
// func (suite *careTeamSuite) CreateCareTeam(c *ie.CareTeam) error {
// 	c.ID = bson.NewObjectId().String()
// 	c.CreatedAt = time.Now()
// 	suite.DB[c.ID] = *c
// 	return nil
// }
//
// func (suite *careTeamSuite) UpdateCareTeam(c *ie.CareTeam) error {
// 	suite.DB[c.ID] = *c
// 	return nil
// }
//
// func (suite *careTeamSuite) DeleteCareTeam(id string) error {
// 	if !bson.IsObjectIdHex(id) {
// 		return errors.New("id was not proper object id hex")
// 	}
// 	if _, ok := suite.DB[id]; !ok {
// 		return errors.New("not found")
// 	}
// 	delete(suite.DB, id)
// 	return nil
// }

// Fixtures

var CareTeamsDB = []ie.CareTeam{
	ie.CareTeam{
		ID:        "58c314acb367c1ff54d19e9e",
		Name:      "The AutoDocs",
		Leader:    "Optometrist Prime",
		CreatedAt: time.Now(),
	},
	ie.CareTeam{
		ID:        "58c314acb367c1ff54d19e9f",
		Name:      "RoboDocs",
		Leader:    "",
		CreatedAt: time.Now(),
	},
}

// Utility Methods

// func (suite *careTeamSuite) withTestService() web.Adapter {
// 	return func(h gin.HandlerFunc) gin.HandlerFunc {
// 		return func(ctx *gin.Context) {
// 			ctx.Set("careTeamService", suite)
// 			h(ctx)
// 		}
// 	}
// }

func findCareTeam(ID string, teams []ie.CareTeam) *ie.CareTeam {
	for _, t := range teams {
		if ID == t.ID {
			return &t
		}
	}
	return nil
}
<commit_msg>fix care team test<commit_after>package web_test

import (
	"encoding/json"
	"errors"
	"net/http"
	"testing"
	"time"

	"gopkg.in/mgo.v2/bson"

	"github.com/gin-gonic/gin"
	"github.com/intervention-engine/ie"
	"github.com/intervention-engine/ie/testutil"
	"github.com/intervention-engine/ie/web"
	"github.com/stretchr/testify/suite"
)

type careTeamSuite struct {
	testutil.WebSuite
	DB map[string]ie.CareTeam
}

func TestCareTeamHandlersSuite(t *testing.T) {
	m := make(map[string]ie.CareTeam)
	suite.Run(t, &careTeamSuite{DB: m})
}

func (suite *careTeamSuite) SetupSuite() {
	api := suite.LoadGin()
	web.RegisterCareTeamRoutes(api, suite.withTestService())
}

func (suite *careTeamSuite) SetupTest() {
	for _, team := range CareTeamsDB {
		suite.DB[team.ID] = team
	}
}

// TODO: Should make some permanent times so we can test that, too
// When there are care teams, should return a slice of care teams with 200 OK
func (suite *careTeamSuite) TestAllCareTeamsFound() {
	w := suite.AssertGetRequest("/api/care_teams", http.StatusOK)

	var body = make(map[string][]ie.CareTeam)
	json.NewDecoder(w.Body).Decode(&body)
	results, ok := body["care_teams"]
	if !ok {
		suite.T().Errorf("body did not contain an element named care_teams | body: %#+v\n", w.Body.String())
		return
	}
	for _, team := range CareTeamsDB {
		result := findCareTeam(team.ID, results)

		suite.Assert().NotNil(result, "body did not contain team: %s with ID: %s\n", team.Name, team.ID)
		suite.Assert().Equal(team.Leader, result.Leader, "team: %s did not contain correct leader | want: %s got: %s", team.Name, team.Leader, result.Leader)
		suite.Assert().Equal(team.Name, result.Name, "team: %s did not contain correct name | want: %s got: %s", team.Name, team.Name, result.Name)
	}
}

// If a care team with that (correct) id does not exist in the database, should
// return 404 Not Found
func (suite *careTeamSuite) TestGetCareTeamNotFound() {
	id := CareTeamsDB[0].ID
	delete(suite.DB, id)
	suite.AssertGetRequest("/api/care_teams/"+id, http.StatusNotFound)
}

// When given an incorrectly formatted id, should return 400 Bad Request
func (suite *careTeamSuite) TestGetCareTeamWithBadId() {
	suite.AssertGetRequest("/api/care_teams/sdf", http.StatusBadRequest)
}

// If a care team with that id exists, should return the care team and 200 OK
func (suite *careTeamSuite) TestGetCareTeamFound() {
	team := CareTeamsDB[0]
	w := suite.AssertGetRequest("/api/care_teams/58c314acb367c1ff54d19e9e", http.StatusOK)
	var body = make(map[string]ie.CareTeam)
	json.NewDecoder(w.Body).Decode(&body)
	result, ok := body["care_team"]
	if !ok {
		suite.T().Errorf("body did not contain an element named care_team | body: %#+v\n", w.Body.String())
		return
	}
	suite.Assert().Equal(team.Leader, result.Leader, "team: %s did not contain correct leader | want: %s got: %s", team.Name, team.Leader, result.Leader)
	suite.Assert().Equal(team.Name, result.Name, "team: %s did not contain correct name | want: %s got: %s", team.Name, team.Name, result.Name)
}

// When called with no form data, should return 400 Bad Request
func TestCreateCareTeamWithNoFormData(t *testing.T) {

}

// When called with correct form data, should return the created object in
// properly formatted JSON
func TestCreateCareTeamReturnsProperJSONOnSuccess(t *testing.T) {

}

// Mock Services

func (suite *careTeamSuite) CareTeam(id string) (*ie.CareTeam, error) {
	if !bson.IsObjectIdHex(id) {
		return nil, errors.New("bad id")
	}
	c, ok := suite.DB[id]
	if !ok {
		return nil, errors.New("not found")
	}
	return &c, nil
}

func (suite *careTeamSuite) CareTeams() ([]ie.CareTeam, error) {
	var cc []ie.CareTeam
	for _, team := range suite.DB {
		cc = append(cc, team)
	}
	return cc, nil
}

func (suite *careTeamSuite) CreateCareTeam(c *ie.CareTeam) error {
	c.ID = bson.NewObjectId().String()
	c.CreatedAt = time.Now()
	suite.DB[c.ID] = *c
	return nil
}

func (suite *careTeamSuite) UpdateCareTeam(c *ie.CareTeam) error {
	suite.DB[c.ID] = *c
	return nil
}

func (suite *careTeamSuite) DeleteCareTeam(id string) error {
	if !bson.IsObjectIdHex(id) {
		return errors.New("id was not proper object id hex")
	}
	if _, ok := suite.DB[id]; !ok {
		return errors.New("not found")
	}
	delete(suite.DB, id)
	return nil
}

// Fixtures

var CareTeamsDB = []ie.CareTeam{
	ie.CareTeam{
		ID:        "58c314acb367c1ff54d19e9e",
		Name:      "The AutoDocs",
		Leader:    "Optometrist Prime",
		CreatedAt: time.Now(),
	},
	ie.CareTeam{
		ID:        "58c314acb367c1ff54d19e9f",
		Name:      "RoboDocs",
		Leader:    "",
		CreatedAt: time.Now(),
	},
}

// Utility Methods

func (suite *careTeamSuite) withTestService() ie.Adapter {
	return func(h gin.HandlerFunc) gin.HandlerFunc {
		return func(ctx *gin.Context) {
			ctx.Set("careTeamService", suite)
			h(ctx)
		}
	}
}

func findCareTeam(ID string, teams []ie.CareTeam) *ie.CareTeam {
	for _, t := range teams {
		if ID == t.ID {
			return &t
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// +build go1.11

/*
 *
 * Copyright 2020 gRPC 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 main starts Greeter service that will response with the hostname.
package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"net"
	"os"
	"strconv"
	"time"

	"google.golang.org/grpc"
	pb "google.golang.org/grpc/examples/helloworld/helloworld"
)

var help = flag.Bool("help", false, "Print usage information")

const (
	defaultPort = 50051
)

// server is used to implement helloworld.GreeterServer.
type server struct {
	pb.UnimplementedGreeterServer

	serverName string
}

func newServer(serverName string) *server {
	return &server{
		serverName: serverName,
	}
}

// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	log.Printf("Received: %v", in.GetName())
	return &pb.HelloReply{Message: "Hello " + in.GetName() + ", from " + s.serverName}, nil
}

func determineHostname() string {
	hostname, err := os.Hostname()
	if err != nil {
		log.Printf("Failed to get hostname: %v, will generate one", err)
		rand.Seed(time.Now().UnixNano())
		return fmt.Sprintf("generated-%03d", rand.Int()%100)
	}
	return hostname
}

func init() {
	flag.Usage = func() {
		fmt.Fprintf(flag.CommandLine.Output(), `
Usage: server [port [hostname]]

  port
        The listen port. Defaults to %d
  hostname
        The name clients will see in greet responses. Defaults to the machine's hostname
`, defaultPort)

		flag.PrintDefaults()
	}
}

func main() {
	flag.Parse()
	if *help {
		flag.Usage()
		return
	}
	args := flag.Args()

	if len(args) > 2 {
		flag.Usage()
		return
	}

	port := defaultPort
	if len(args) > 0 {
		var err error
		port, err = strconv.Atoi(args[0])
		if err != nil {
			log.Printf("Invalid port number: %v", err)
			flag.Usage()
			return
		}
	}

	var hostname string
	if len(args) > 1 {
		hostname = args[1]
	}
	if hostname == "" {
		hostname = determineHostname()
	}

	lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, newServer(hostname))
	log.Printf("serving on %s, hostname %s", lis.Addr(), hostname)
	s.Serve(lis)
}
<commit_msg>xds: add reflection and health service to example server (#3403)<commit_after>// +build go1.11

/*
 *
 * Copyright 2020 gRPC 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 main starts Greeter service that will response with the hostname.
package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"net"
	"os"
	"strconv"
	"time"

	"google.golang.org/grpc"
	pb "google.golang.org/grpc/examples/helloworld/helloworld"
	"google.golang.org/grpc/health"
	healthpb "google.golang.org/grpc/health/grpc_health_v1"
	"google.golang.org/grpc/reflection"
)

var help = flag.Bool("help", false, "Print usage information")

const (
	defaultPort = 50051
)

// server is used to implement helloworld.GreeterServer.
type server struct {
	pb.UnimplementedGreeterServer

	serverName string
}

func newServer(serverName string) *server {
	return &server{
		serverName: serverName,
	}
}

// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	log.Printf("Received: %v", in.GetName())
	return &pb.HelloReply{Message: "Hello " + in.GetName() + ", from " + s.serverName}, nil
}

func determineHostname() string {
	hostname, err := os.Hostname()
	if err != nil {
		log.Printf("Failed to get hostname: %v, will generate one", err)
		rand.Seed(time.Now().UnixNano())
		return fmt.Sprintf("generated-%03d", rand.Int()%100)
	}
	return hostname
}

func init() {
	flag.Usage = func() {
		fmt.Fprintf(flag.CommandLine.Output(), `
Usage: server [port [hostname]]

  port
        The listen port. Defaults to %d
  hostname
        The name clients will see in greet responses. Defaults to the machine's hostname
`, defaultPort)

		flag.PrintDefaults()
	}
}

func main() {
	flag.Parse()
	if *help {
		flag.Usage()
		return
	}
	args := flag.Args()

	if len(args) > 2 {
		flag.Usage()
		return
	}

	port := defaultPort
	if len(args) > 0 {
		var err error
		port, err = strconv.Atoi(args[0])
		if err != nil {
			log.Printf("Invalid port number: %v", err)
			flag.Usage()
			return
		}
	}

	var hostname string
	if len(args) > 1 {
		hostname = args[1]
	}
	if hostname == "" {
		hostname = determineHostname()
	}

	lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", port))
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, newServer(hostname))

	reflection.Register(s)
	healthServer := health.NewServer()
	healthServer.SetServingStatus("", healthpb.HealthCheckResponse_SERVING)
	healthpb.RegisterHealthServer(s, healthServer)

	log.Printf("serving on %s, hostname %s", lis.Addr(), hostname)
	s.Serve(lis)
}
<|endoftext|>