func TestState_ClusterExists(t *testing.T) {
	t.Parallel()

	testPrefix := "TestState_ClusterExists"
	testutil.ResetEtcd(t, testPrefix)
	logger := testutil.NewTestLogger(testPrefix, t)

	state, err := NewStateEtcdWithPrefix(testutil.LocalEtcdConfig, testPrefix, logger)
	if err != nil {
		t.Fatalf("Could not create state", err)
	}

	clusterID := structs.ClusterID(uuid.New())
	planID := uuid.New()
	clusterState := structs.ClusterState{
		InstanceID:       clusterID,
		OrganizationGUID: "OrganizationGUID",
		PlanID:           planID,
		ServiceID:        "ServiceID",
		SpaceGUID:        "SpaceGUID",
	}
	err = state.SaveCluster(clusterState)
	if err != nil {
		t.Fatalf("SaveCluster failed %s", err)
	}

	if !state.ClusterExists(clusterID) {
		t.Fatalf("Cluster %s should exist", clusterID)
	}

	if state.ClusterExists("fakeID") {
		t.Fatalf("Cluster %s should not exist", "fakeID")
	}
}
func TestState_LoadCluster(t *testing.T) {
	t.Parallel()

	testPrefix := "TestState_LoadClusterState"
	testutil.ResetEtcd(t, testPrefix)
	logger := testutil.NewTestLogger(testPrefix, t)

	state, err := NewStateEtcdWithPrefix(testutil.LocalEtcdConfig, testPrefix, logger)
	if err != nil {
		t.Fatalf("Could not create state", err)
	}

	instanceID := structs.ClusterID(uuid.New())
	planID := uuid.New()
	clusterState := structs.ClusterState{
		InstanceID:       instanceID,
		OrganizationGUID: "OrganizationGUID",
		PlanID:           planID,
		ServiceID:        "ServiceID",
		SpaceGUID:        "SpaceGUID",
	}
	err = state.SaveCluster(clusterState)
	if err != nil {
		t.Fatalf("SaveCluster failed %s", err)
	}

	loadedState, err := state.LoadCluster(instanceID)
	if !reflect.DeepEqual(clusterState, loadedState) {
		t.Fatalf("Failed to load ClusterState")
	}
}
func TestRouter_RemoveClusterAssignement(t *testing.T) {
	t.Parallel()

	testPrefix := "TestRouter_RemoveClusterAssignement"
	etcdApi := resetEtcd(t, testPrefix)
	logger := testutil.NewTestLogger(testPrefix, t)

	clusterID := structs.ClusterID("clusterID")
	port := 30000

	key := fmt.Sprintf("%s/routing/allocation/%s", testPrefix, clusterID)
	_, err := etcdApi.Set(context.Background(), key, fmt.Sprintf("%d", port), &etcd.SetOptions{})

	router, err := NewRouterWithPrefix(testutil.LocalEtcdConfig, testPrefix, logger)
	if err != nil {
		t.Fatalf("Could not create a new router", err)
	}

	err = router.RemoveClusterAssignment(clusterID)
	if err != nil {
		t.Fatalf("Could not remove the assignment %s", err)
	}

	_, err = etcdApi.Get(context.Background(), fmt.Sprintf("%s/routing/allocation/%s", testPrefix, clusterID), &etcd.GetOptions{})
	if err == nil {
		t.Fatalf("port wasn't deleted %s", err)
	}
}
func TestRouter_AssignPortToCluster(t *testing.T) {
	t.Parallel()

	testPrefix := "TestRouter_AssignPortToCluster"
	etcdApi := resetEtcd(t, testPrefix)
	logger := testutil.NewTestLogger(testPrefix, t)

	router, err := NewRouterWithPrefix(testutil.LocalEtcdConfig, testPrefix, logger)
	if err != nil {
		t.Fatalf("Could not create a new router", err)
	}

	clusterID := structs.ClusterID("clusterID")
	port := 30000
	err = router.AssignPortToCluster(clusterID, port)
	if err != nil {
		t.Fatalf("Assigning port failed")
	}

	key := fmt.Sprintf("%s/routing/allocation/%s", testPrefix, clusterID)
	resp, err := etcdApi.Get(context.Background(), key, &etcd.GetOptions{})
	if err != nil {
		t.Fatalf("Could not read port from etcd")
	}

	retrievedPort, err := strconv.Atoi(resp.Node.Value)
	if want, got := port, retrievedPort; want != got {
		t.Fatalf("Routing was not initialized. Expected %d, got %d", want, got)
	}
}
// LoadAllRunningClusters fetches the /state information for all running clusters
func (s *StateEtcd) LoadAllRunningClusters() (clusters []*structs.ClusterState, err error) {
	ctx := context.Background()
	servicesKey := fmt.Sprintf("%s/service", s.prefix)
	services, err := s.etcdApi.Get(ctx, servicesKey, &etcd.GetOptions{Recursive: false})
	if err != nil {
		return
	}

	instanceIDRegExp, _ := regexp.Compile("/service/(.*)")
	for _, service := range services.Node.Nodes {
		instanceID := instanceIDRegExp.FindStringSubmatch(service.Key)[1]
		cluster, _ := s.LoadCluster(structs.ClusterID(instanceID))
		clusters = append(clusters, &cluster)
	}
	return
}
func adminServiceInstances(bkr *Broker, router httpRouter, logger lager.Logger) http.HandlerFunc {
	return func(w http.ResponseWriter, req *http.Request) {
		vars := router.Vars(req)
		instanceID := structs.ClusterID(vars["instance_id"])

		logger := bkr.newLoggingSession("admin.service-instances", lager.Data{"instance-id": instanceID})
		defer logger.Info("done")

		cluster, err := bkr.state.LoadCluster(instanceID)
		if err != nil {
			logger.Error("load-cluster.error", err)
			respond(w, http.StatusInternalServerError, err.Error())
		}

		respond(w, http.StatusOK, cluster)
	}
}
func TestState_DeleteCluster(t *testing.T) {
	t.Parallel()

	testPrefix := "TestState_DeleteClusterState"
	etcdApi := testutil.ResetEtcd(t, testPrefix)
	logger := testutil.NewTestLogger(testPrefix, t)

	state, err := NewStateEtcdWithPrefix(testutil.LocalEtcdConfig, testPrefix, logger)
	if err != nil {
		t.Fatalf("Could not create state", err)
	}

	instanceID := structs.ClusterID(uuid.New())
	planID := uuid.New()
	clusterState := structs.ClusterState{
		InstanceID:       instanceID,
		OrganizationGUID: "OrganizationGUID",
		PlanID:           planID,
		ServiceID:        "ServiceID",
		SpaceGUID:        "SpaceGUID",
	}
	err = state.SaveCluster(clusterState)
	if err != nil {
		t.Fatalf("SaveCluster failed %s", err)
	}

	err = state.DeleteCluster(instanceID)
	if err != nil {
		t.Fatalf("DeleteClusterState failed %s", err)
	}

	key := fmt.Sprintf("%s/service/%s/state", testPrefix, instanceID)
	_, err = etcdApi.Get(context.Background(), key, &etcd.GetOptions{})
	if err == nil {
		t.Fatalf("Was expecting error 'Key not found'")
	} else {
		notFoundRegExp, _ := regexp.Compile("Key not found")
		if notFoundRegExp.FindString(err.Error()) != "Key not found" {
			t.Fatalf("An error other than 'Key not found' occured %s", err)
		}
	}
}
func TestState_LoadCluster(t *testing.T) {
	t.Parallel()

	testPrefix := "TestState_LoadClusterState"
	etcdApi := testutil.ResetEtcd(t, testPrefix)
	logger := testutil.NewTestLogger(testPrefix, t)

	state, err := NewStateEtcdWithPrefix(testutil.LocalEtcdConfig, testPrefix, logger)
	if err != nil {
		t.Fatalf("Could not create state", err)
	}

	instanceID := structs.ClusterID(uuid.New())
	planID := uuid.New()

	node := structs.Node{ID: "node_id", CellGUID: "cell_guid"}
	clusterState := structs.ClusterState{
		InstanceID:       instanceID,
		OrganizationGUID: "OrganizationGUID",
		PlanID:           planID,
		ServiceID:        "ServiceID",
		SpaceGUID:        "SpaceGUID",
		SchedulingInfo: structs.SchedulingInfo{
			Status: structs.SchedulingStatusInProgress,
		},
	}
	clusterState.Nodes = []*structs.Node{&node}
	err = state.SaveCluster(clusterState)
	if err != nil {
		t.Fatalf("SaveCluster failed %s", err)
	}
	data, err := json.Marshal(node)
	key := fmt.Sprintf(
		"/%s/service/%s/nodes/%s", testPrefix, clusterState.InstanceID, node.ID)
	etcdApi.Set(context.Background(), key, string(data), &etcd.SetOptions{})

	loadedState, err := state.LoadCluster(instanceID)
	if !reflect.DeepEqual(clusterState, loadedState) {
		t.Fatalf("Failed to load ClusterState. Expected: %v, actual: %v", clusterState, loadedState)
	}
}
func TestState_SaveCluster(t *testing.T) {
	t.Parallel()

	testPrefix := "TestState_SaveCluster"
	etcdApi := testutil.ResetEtcd(t, testPrefix)
	logger := testutil.NewTestLogger(testPrefix, t)

	state, err := NewStateEtcdWithPrefix(testutil.LocalEtcdConfig, testPrefix, logger)
	if err != nil {
		t.Fatalf("Could not create state", err)
	}

	clusterID := structs.ClusterID(uuid.New())
	planID := uuid.New()
	clusterState := structs.ClusterState{
		InstanceID:       clusterID,
		OrganizationGUID: "OrganizationGUID",
		PlanID:           planID,
		ServiceID:        "ServiceID",
		SpaceGUID:        "SpaceGUID",
	}
	err = state.SaveCluster(clusterState)
	if err != nil {
		t.Fatalf("SaveCluster failed %s", err)
	}

	resp, err := etcdApi.Get(context.Background(), fmt.Sprintf("%s/service/%s/state", testPrefix, clusterID), &etcd.GetOptions{})
	if err != nil {
		t.Fatalf("Could not load state from etcd %s", err)
	}

	retrievedState := structs.ClusterState{}
	json.Unmarshal([]byte(resp.Node.Value), &retrievedState)
	if !reflect.DeepEqual(clusterState, retrievedState) {
		t.Fatalf("Retrieved State does not match. Want %v, Got %v", clusterState, retrievedState)
	}
}
func TestCallbacks_RestoreRecreationData(t *testing.T) {
	t.Parallel()

	testPrefix := "TestCallbacks_WriteRecreationData"
	logger := testutil.NewTestLogger(testPrefix, t)

	testDir, err := ioutil.TempDir("", testPrefix)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	defer os.Remove(testDir)
	fileName := fmt.Sprintf("%s/%s", testDir, testPrefix)

	instanceID := structs.ClusterID(uuid.New())
	recreationData := &structs.ClusterRecreationData{
		InstanceID:       instanceID,
		OrganizationGUID: "OrganizationGUID",
		PlanID:           "PlanID",
		ServiceID:        "ServiceID",
		SpaceGUID:        "SpaceGUID",
		AdminCredentials: structs.PostgresCredentials{
			Username: "******",
			Password: "******",
		},
		AllocatedPort: 1234,
	}

	dataRaw, _ := json.Marshal(recreationData)
	err = ioutil.WriteFile(fileName, dataRaw, os.ModePerm)
	if err != nil {
		t.Fatalf("Could not write file")
	}

	cfg := config.Callbacks{
		ClusterDataRestore: &config.CallbackCommand{
			Command:   "cat",
			Arguments: []string{fileName},
		},
	}

	// test that it retrieves the data from stdout
	callbacks := NewCallbacks(cfg, logger)
	restoredData, err := callbacks.RestoreRecreationData(instanceID)
	if err != nil {
		t.Fatalf("Could not open file %s, Err: %s", fileName, err)
	}

	if !reflect.DeepEqual(recreationData, restoredData) {
		t.Fatalf("Retrieved Data doesn't equal original. %v != %v", restoredData, recreationData)
	}

	// test that it passes instanceID to stdin
	cfg = config.Callbacks{
		ClusterDataRestore: &config.CallbackCommand{
			Command:   "tee",
			Arguments: []string{fileName},
		},
	}
	callbacks = NewCallbacks(cfg, logger)
	_, err = callbacks.RestoreRecreationData(instanceID)

	fileContent, err := ioutil.ReadFile(fileName)
	if err != nil {
		t.Fatalf("Could not open file %s, Err: %s", fileName, err)
	}

	if structs.ClusterID(fileContent) != instanceID {
		t.Fatalf("InstanceID %s was not passed via stdin", instanceID)
	}
}
// Bind returns access credentials for a service instance
func (bkr *Broker) Bind(instanceID string, bindingID string, details brokerapi.BindDetails) (brokerapi.BindingResponse, error) {
	return bkr.bind(structs.ClusterID(instanceID), bindingID, details)
}
// Provision a new service instance
func (bkr *Broker) Provision(instanceID string, details brokerapi.ProvisionDetails, acceptsIncomplete bool) (resp brokerapi.ProvisioningResponse, async bool, err error) {
	return bkr.provision(structs.ClusterID(instanceID), details, acceptsIncomplete)
}
// LastOperation returns the status of the last operation on a service instance
// TODO: Plan needs to progressively store the state/description/error message; then
// LastOperation fetches it and returns it (rather than doing any calculations of its own)
// TODO: AddNode, AddNode, WaitForAllMembersRunning
func (bkr *Broker) LastOperation(instanceID string) (resp brokerapi.LastOperationResponse, err error) {
	return bkr.lastOperation(structs.ClusterID(instanceID))
}
// Deprovision service instance
func (bkr *Broker) Deprovision(instanceID string, details brokerapi.DeprovisionDetails, acceptsIncomplete bool) (async bool, err error) {
	return bkr.deprovision(structs.ClusterID(instanceID), details, acceptsIncomplete)
}
// Update service instance
func (bkr *Broker) Update(instanceID string, updateDetails brokerapi.UpdateDetails, acceptsIncomplete bool) (async bool, err error) {
	return bkr.update(structs.ClusterID(instanceID), updateDetails, acceptsIncomplete)
}