Exemplo n.º 1
0
/*
POST /databases/register
PUT /databases/assign
*/
func DatabasesHandler(w http.ResponseWriter, request *http.Request) {
	vars := mux.Vars(request)
	log.Trace(fmt.Sprintf("admin.DatabasesHandler() > %s /databases/%s %+v", request.Method, vars["action"], vars))
	switch request.Method {
	case "GET":
		switch vars["action"] {
		case "": // List All Databases
			instances, err := instances.All()
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`, http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): instances.All() %s %+v ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
				return
			}
			jsonInstances, err := json.Marshal(instances)
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`, http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): json.Marshal(instances) %s %+v ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
			} else {
				w.Header().Set("Content-Type", "application/json; charset=UTF-8")
				w.WriteHeader(http.StatusOK)
				w.Write(jsonInstances)
			}
		case "available": // Lists Available Databases
			instances, err := instances.Available()
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`, http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): instances.Available() %s %+v ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
				return
			}
			jsonInstances, err := json.Marshal(instances)
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`, http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): json.Marshal(instances) %s %+v ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
			} else {
				w.Header().Set("Content-Type", "application/json; charset=UTF-8")
				w.WriteHeader(http.StatusOK)
				w.Write(jsonInstances)
			}
		default:
			msg := fmt.Sprintf(`{"status": %d, "description": "Invalid Action %s"}`, http.StatusBadRequest, vars["action"])
			log.Error(fmt.Sprintf(`admin.DatabasesHandler(): %s %s`, msg, vars))
			http.Error(w, msg, http.StatusBadRequest)
		}
	case `POST`:
		var i instances.Instance
		decoder := json.NewDecoder(request.Body)
		err := decoder.Decode(&i)
		if err != nil {
			msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`, http.StatusInternalServerError, err)
			log.Error(fmt.Sprintf(`admin.DatabasesHandler(): decoder.Decode() %s %s ! %s`, msg, vars, err))
			http.Error(w, msg, http.StatusInternalServerError)
			return
		}
		switch vars[`action`] {
		case `register`: // Creates a new record.
			err = i.Register()
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`, http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): Instance#Register() %s %s ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
				return
			} else {
				w.Header().Set(`Content-Type`, `application/json; charset=UTF-8`)
				w.WriteHeader(http.StatusOK)
				w.Write([]byte(`{}`))
				return
			}
		default:
			msg := fmt.Sprintf(`{"status": %d, "description": "Invalid Action %s"}`, http.StatusBadRequest, vars[`action`])
			log.Error(msg)
			http.Error(w, msg, http.StatusBadRequest)
		}
	case `PUT`:
		var i instances.Instance
		decoder := json.NewDecoder(request.Body)
		err := decoder.Decode(&i)
		if err != nil {
			msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`, http.StatusInternalServerError, err)
			log.Error(fmt.Sprintf(`admin.DatabasesHandler(): decoder.Decode() %s %s ! %s`, msg, vars, err))
			http.Error(w, msg, http.StatusInternalServerError)
			return
		}

		switch vars[`action`] {
		case `assign`: // updates an existing record.
			err = i.Assign()
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`, http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): instances.Assign() %s %s ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
				return
			} else {
				w.Header().Set(`Content-Type`, `application/json; charset=UTF-8`)
				w.WriteHeader(http.StatusOK)
				w.Write([]byte(`{}`))
				return
			}
		default:
			msg := fmt.Sprintf(`{"status": %d, "description": "Invalid Action %s"}`, http.StatusBadRequest, vars[`action`])
			log.Error(fmt.Sprintf(`admin.DatabasesHandler(): %s %s`, msg, vars))
			http.Error(w, msg, http.StatusBadRequest)
		}
	case `DELETE`:
		switch vars[`action`] {
		case `decommission`:
			i, err := instances.FindByDatabase(vars[`database`])
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`, http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): instance.FindByDatabase() %s %s ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
				return
			} else {
				err = i.Decommission()
				if err != nil {
					msg := fmt.Sprintf(`{"status": %d, "description": "There was an error decommissioning the database (%s)"}`, http.StatusInternalServerError, err)
					log.Error(fmt.Sprintf(`admin.DatabasesHandler(): instance#Decommission() %s %s ! %s`, msg, vars, err))
					http.Error(w, msg, http.StatusInternalServerError)
				}
			}
		default:
			msg := fmt.Sprintf(`{"status": %d, "description": "Invalid Action %s"}`, http.StatusBadRequest, vars[`action`])
			log.Error(fmt.Sprintf(`admin.DatabasesHandler(): %s %s`, msg, vars))
			http.Error(w, msg, http.StatusBadRequest)
		}
	default:
		msg := fmt.Sprintf(`{"status": %d, "description": "Method not allowed %s"}`, http.StatusMethodNotAllowed, request.Method)
		log.Error(fmt.Sprintf(`admin.DatabasesHandler(): %s %s`, msg, vars))
		http.Error(w, msg, http.StatusMethodNotAllowed)
		return
	}
}
Exemplo n.º 2
0
/*
PrecreateDatabases is called as a scheduled task for precreating databaes.
*/
func (t *Task) PrecreateDatabases() (err error) {
	if globals.ServiceRole != "service" { //Safety valve...
		log.Error(fmt.Sprintf("tasks.Task#PrecreateDatabases() ! Not precreating databases as we are not running on a service node..."))
		return
	}
	t.ClusterID = os.Getenv(`RDPGD_CLUSTER`)
	if t.ClusterID == "" {
		matrixName := os.Getenv(`RDPGD_MATRIX`)
		matrixNameSplit := strings.SplitAfterN(matrixName, `-`, -1)
		matrixColumn := os.Getenv(`RDPGD_MATRIX_COLUMN`)
		for i := 0; i < len(matrixNameSplit)-1; i++ {
			t.ClusterID = t.ClusterID + matrixNameSplit[i]
		}
		t.ClusterID = t.ClusterID + "c" + matrixColumn
	}

	client, err := consulapi.NewClient(consulapi.DefaultConfig())
	if err != nil {
		log.Error(fmt.Sprintf("tasks.Task#PrecreateDatabases() ! %s", err))
		return
	}
	// Lock Database Creation (and Deletion) via Consul Lock
	key := fmt.Sprintf(`rdpg/%s/database/existance/lock`, t.ClusterID)
	lo := &consulapi.LockOptions{
		Key:         key,
		SessionName: fmt.Sprintf(`rdpg/%s/databases/existance`, t.ClusterID),
	}
	log.Trace(fmt.Sprintf(`tasks.Task<%s>#PrecreateDatabases() Attempting to acquire database existance lock %s...`, t.ClusterID, key))
	databaseCreateLock, err := client.LockOpts(lo)
	if err != nil {
		log.Error(fmt.Sprintf(`tasks.Task<%s>#PrecreateDatabases() LockKey() Error locking database existance Key %s ! %s`, t.ClusterID, key, err))
		return
	}
	databaseCreateLockCh, err := databaseCreateLock.Lock(nil)
	if err != nil {
		log.Error(fmt.Sprintf(`tasks.Task<%s>#PrecreateDatabases() Lock() database/existance lock %s ! %s`, t.ClusterID, key, err))
		return
	}
	defer databaseCreateLock.Unlock()
	if databaseCreateLockCh == nil {
		log.Error(fmt.Sprintf(`tasks.Task<%s>#PrecreateDatabases() database/existance Lock not aquired, halting Creation!!!`, t.ClusterID))
		return
	}

	// We have the database existance lock...
	key = fmt.Sprintf(`rdpg/%s/capacity/instances/limit`, t.ClusterID)
	kv := client.KV()
	kvp, _, err := kv.Get(key, nil)
	if err != nil {
		log.Error(fmt.Sprintf(`tasks.Task<%s>#PrecreateDatabases() kv.Get(%s) ! %s`, t.ClusterID, key, err))
		return
	}
	if kvp == nil {
		log.Trace(fmt.Sprintf(`tasks.Task<%s>#PrecreateDatabases() kv.Get(%s) : key is not set...`, t.ClusterID, key))
		return
	}
	maxLimitString := string(kvp.Value)
	maxLimit, err := strconv.Atoi(maxLimitString)
	if err != nil {
		log.Error(fmt.Sprintf(`tasks.Task<%s>#PrecreateDatabases() strconv.Atoi(%s) (Limit)`, t.ClusterID, kvp.Value))
		return
	}
	log.Trace(fmt.Sprintf(`tasks.Task<%s>#PrecreateDatabases() retrived max_instances_limit: %d`, t.ClusterID, maxLimit))

	key = fmt.Sprintf(`rdpg/%s/capacity/instances/allowed`, t.ClusterID)
	kvp, _, err = kv.Get(key, nil)
	if err != nil {
		log.Error(fmt.Sprintf(`tasks.Task<%s>#PrecreateDatabases() kvp.Get(%s) ! %s`, t.ClusterID, key, err))
		return
	}
	if kvp == nil {
		log.Trace(fmt.Sprintf(`tasks.Task<%s>#PrecreateDatabases() kv.Get(%s) : key is not set...`, t.ClusterID, key))
		return
	}
	maxAllowedString := string(kvp.Value)
	maxAllowed, err := strconv.Atoi(maxAllowedString)
	if err != nil {
		log.Error(fmt.Sprintf(`tasks.Task<%s>#PrecreateDatabases() strconv.Atoi(%s) (Allowed)`, t.ClusterID, kvp.Value))
		return
	}
	log.Trace(fmt.Sprintf(`tasks.Task<%s>#PrecreateDatabases() retrived max_instances_allowed: %d`, t.ClusterID, maxAllowed))

	maxCapacity := maxAllowed
	if maxLimit < maxCapacity {
		maxCapacity = maxLimit
	}

	i, err := instances.Available()
	if err != nil {
		log.Error(fmt.Sprintf("tasks.Task<%s>#PrecreateDatabases() instances.Available()! %s", t.ClusterID, err))
		return
	}
	numAvailable := len(i)
	//i, err = instances.All()
	i, err = instances.Undecommissioned()
	if err != nil {
		log.Error(fmt.Sprintf("tasks.Task<%s>#PrecreateDatabases() instances.Undecommissioned() ! %s", t.ClusterID, err))
		return
	}
	numInstances := len(i)
	totalNeeded := poolSize - numAvailable

	if len(t.Data) != 0 { // Admin creating a database.
		// In this case we were called with a number to precreate, such as from
		// admin api "precreate 100":  for 1 .. N TODO: admin API endpoint for this
		n, err := strconv.Atoi(t.Data)
		if err != nil {
			log.Error(fmt.Sprintf("tasks.Task#PrecreateDatabases() strconv.Atoi(%s) (t.Data) ! %s", t.Data, err))
		} else {
			totalNeeded = n // TODO: Account for maxAllowed
		}
	}

	log.Trace(fmt.Sprintf("tasks.Task#PrecreateDatabases() The total databases which need to be Precreated is %d, based on there being %d instances already, %d unused databases, a desired pool size of %d and a maximum capacity of %d...", totalNeeded, numInstances, numAvailable, poolSize, maxCapacity))

	if totalNeeded > 0 {
		totalCreated := numInstances
		for index := 0; (index < totalNeeded) && (totalCreated < maxCapacity); index++ {
			log.Trace(fmt.Sprintf("tasks.Task#PrecreateDatabases() Precreating a new database for cluster type %s...", t.ClusterService))
			if t.ClusterService == "pgbdr" {
				err = t.bdrPrecreateDatabase(client)
			} else if t.ClusterService == "postgresql" {
				err = t.postgresqlPrecreateDatabase(client)
			} else {
				log.Error(fmt.Sprintf("tasks.Task#PrecreateDatabases() Bad ClusterService for Precreate ! Attempted to determine the service cluster type and do not know how to handle type '%s' ", t.ClusterService))
				return
			}
			if err != nil {
				log.Error(fmt.Sprintf("tasks.Task#PrecreateDatabases() t.PrecreateDatabases() ! %s", err))
				return err
			}
			totalCreated++
		}
	}

	return
}
Exemplo n.º 3
0
/*
POST /databases/register
PUT /databases/assign
*/
func DatabasesHandler(w http.ResponseWriter, request *http.Request) {
	vars := mux.Vars(request)
	log.Trace(fmt.Sprintf("admin.DatabasesHandler() > %s /databases/%s %+v", request.Method, vars["action"], vars))
	switch request.Method {
	case "GET":
		switch vars["action"] {
		case "": // List All Databases
			instances, err := instances.All()
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`+"\n", http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): instances.All() %s %+v ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
				return
			}
			jsonInstances, err := json.Marshal(instances)
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`+"\n", http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): json.Marshal(instances) %s %+v ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
			} else {
				w.Header().Set("Content-Type", "application/json; charset=UTF-8")
				w.WriteHeader(http.StatusOK)
				w.Write(jsonInstances)
			}
		case "available": // Lists Available Databases
			instances, err := instances.Available()
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`+"\n", http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): instances.Available() %s %+v ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
				return
			}
			jsonInstances, err := json.Marshal(instances)
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`+"\n", http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): json.Marshal(instances) %s %+v ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
			} else {
				w.Header().Set("Content-Type", "application/json; charset=UTF-8")
				w.WriteHeader(http.StatusOK)
				w.Write(jsonInstances)
			}
		default:
			msg := fmt.Sprintf(`{"status": %d, "description": "Invalid Action %s"}`+"\n", http.StatusBadRequest, vars["action"])
			log.Error(fmt.Sprintf(`admin.DatabasesHandler(): %s %s`, msg, vars))
			http.Error(w, msg, http.StatusBadRequest)
		}
	case `POST`:
		var i instances.Instance
		decoder := json.NewDecoder(request.Body)
		err := decoder.Decode(&i)
		if err != nil {
			msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`+"\n", http.StatusInternalServerError, err)
			log.Error(fmt.Sprintf(`admin.DatabasesHandler(): decoder.Decode() %s %s ! %s`, msg, vars, err))
			http.Error(w, msg, http.StatusInternalServerError)
			return
		}
		switch vars[`action`] {
		case `register`:
			err = i.Register()
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`+"\n", http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): Instance#Register() %s %s ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
				return
			} else {
				w.Header().Set(`Content-Type`, `application/json; charset=UTF-8`)
				w.WriteHeader(http.StatusOK)
				w.Write([]byte(`{}`))
				return
			}
		default:
			msg := fmt.Sprintf(`{"status": %d, "description": "Invalid Action %s"}`+"\n", http.StatusBadRequest, vars[`action`])
			log.Error(msg)
			http.Error(w, msg, http.StatusBadRequest)
		}
	case `PUT`:
		switch vars[`action`] {
		case `assign`: // updates an existing record.
			// PUT /databases/assign/database
			var i instances.Instance
			decoder := json.NewDecoder(request.Body)
			err := decoder.Decode(&i)
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`+"\n", http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): decoder.Decode() assign %s %s ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
				return
			}
			err = i.Assign()
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`+"\n", http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): instances.Assign() %s %s ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
				return
			} else {
				w.Header().Set(`Content-Type`, `application/json; charset=UTF-8`)
				w.WriteHeader(http.StatusOK)
				w.Write([]byte(`{}`))
				return
			}
		case `decommissioned`: // updates an existing record to show it was deprovisioned.
			// PUT /databases/decommissioned
			// This is requested from service cluster to master cluster
			type decomm struct {
				Database  string `json:"database"`
				Timestamp string `json:"timestamp"`
			}
			dc := decomm{}
			decoder := json.NewDecoder(request.Body)
			err := decoder.Decode(&dc)
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`+"\n", http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): decoder.Decode() decommissioned %s %s ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
				return
			}

			if len(dc.Timestamp) < 1 {
				err = errors.New(`Timestamp query parameter assignment is required!`)
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`+"\n", http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): decommissioned %s ! %s`, msg, err))
				http.Error(w, msg, http.StatusInternalServerError)
				return
			}

			i, err := instances.FindByDatabase(dc.Database)
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`+"\n", http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): instances.FindByDatabase(%s) %s ! %s`, dc.Database, msg, err))
				http.Error(w, msg, http.StatusInternalServerError)
				return
			}

			err = i.DecommissionedAt(dc.Timestamp)
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}"`+"\n", http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): instances.DecommissionedAt() %s %s ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
				return
			} else {
				w.Header().Set(`Content-Type`, `application/json; charset=UTF-8`)
				w.WriteHeader(http.StatusOK)
				w.Write([]byte(`{}`))
				return
			}
		default:
			msg := fmt.Sprintf(`{"status": %d, "description": "Invalid Action %s"}`+"\n", http.StatusBadRequest, vars[`action`])
			log.Error(fmt.Sprintf(`admin.DatabasesHandler(): %s %s`, msg, vars))
			http.Error(w, msg, http.StatusBadRequest)
		}
	case `DELETE`:
		switch vars[`action`] {
		case `decommission`:
			i, err := instances.FindByDatabase(vars[`database`])
			if err != nil {
				msg := fmt.Sprintf(`{"status": %d, "description": "%s"}`+"\n", http.StatusInternalServerError, err)
				log.Error(fmt.Sprintf(`admin.DatabasesHandler(): instance.FindByDatabase() %s %s ! %s`, msg, vars, err))
				http.Error(w, msg, http.StatusInternalServerError)
				return
			} else {
				err = i.Decommission()
				if err != nil {
					msg := fmt.Sprintf(`{"status": %d, "description": "There was an error decommissioning the database (%s)"}`+"\n", http.StatusInternalServerError, err)
					log.Error(fmt.Sprintf(`admin.DatabasesHandler(): instance#Decommission() %s %s ! %s`, msg, vars, err))
					http.Error(w, msg, http.StatusInternalServerError)
				}
			}
		default:
			msg := fmt.Sprintf(`{"status": %d, "description": "Invalid Action %s"}`+"\n", http.StatusBadRequest, vars[`action`])
			log.Error(fmt.Sprintf(`admin.DatabasesHandler(): %s %s`, msg, vars))
			http.Error(w, msg, http.StatusBadRequest)
		}
	default:
		msg := fmt.Sprintf(`{"status": %d, "description": "Method not allowed %s"}`+"\n", http.StatusMethodNotAllowed, request.Method)
		log.Error(fmt.Sprintf(`admin.DatabasesHandler(): %s %s`, msg, vars))
		http.Error(w, msg, http.StatusMethodNotAllowed)
		return
	}
}