add setup service
parent
96129d717f
commit
3addadc12a
|
@ -25,6 +25,7 @@ func init() {
|
||||||
influxCmd.AddCommand(queryCmd)
|
influxCmd.AddCommand(queryCmd)
|
||||||
influxCmd.AddCommand(organizationCmd)
|
influxCmd.AddCommand(organizationCmd)
|
||||||
influxCmd.AddCommand(userCmd)
|
influxCmd.AddCommand(userCmd)
|
||||||
|
influxCmd.AddCommand(setupCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flags contains all the CLI flag values for influx.
|
// Flags contains all the CLI flag values for influx.
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/influxdata/platform"
|
||||||
|
"github.com/influxdata/platform/cmd/influx/internal"
|
||||||
|
"github.com/influxdata/platform/http"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// setup Command
|
||||||
|
var setupCmd = &cobra.Command{
|
||||||
|
Use: "setup",
|
||||||
|
Short: "Create default username, password, org, bucket...",
|
||||||
|
Run: setupF,
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupF(cmd *cobra.Command, args []string) {
|
||||||
|
// check if setup is allowed
|
||||||
|
s := &http.SetupService{
|
||||||
|
Addr: flags.host,
|
||||||
|
}
|
||||||
|
|
||||||
|
allowed, err := s.IsOnboarding(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if !allowed {
|
||||||
|
fmt.Println("Initialization has been already completed")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
or := new(platform.OnboardingRequest)
|
||||||
|
|
||||||
|
fmt.Println("Welcome to influxdata platform!")
|
||||||
|
or.User = getInput("Please type your primary username.\r\nOr ENTER to use \"admin\":", "admin", false)
|
||||||
|
or.Password = getInput("Please type your password:", "", true)
|
||||||
|
or.Org = getInput("Please type your primary organization name.\r\nOr ENTER to use \"default\":", "default", false)
|
||||||
|
or.Bucket = getInput("Please type your primary bucket name.\r\nOr ENTER to use \"default\":", "default", false)
|
||||||
|
fmt.Println(or)
|
||||||
|
|
||||||
|
result, err := s.Generate(context.Background(), or)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
w := internal.NewTabWriter(os.Stdout)
|
||||||
|
w.WriteHeaders(
|
||||||
|
"UserID",
|
||||||
|
"UserName",
|
||||||
|
"Organization",
|
||||||
|
"Bucket",
|
||||||
|
"Token",
|
||||||
|
)
|
||||||
|
w.Write(map[string]interface{}{
|
||||||
|
"UserID": result.User.ID.String(),
|
||||||
|
"UserName": result.User.Name,
|
||||||
|
"Organization": result.Org.Name,
|
||||||
|
"Bucket": result.Bucket.Name,
|
||||||
|
"Token": result.Auth.Token,
|
||||||
|
})
|
||||||
|
w.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getInput(prompt, defaultValue string, isPassword bool) string {
|
||||||
|
var line string
|
||||||
|
oldState, err := terminal.MakeRaw(0)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer terminal.Restore(0, oldState)
|
||||||
|
term := terminal.NewTerminal(struct {
|
||||||
|
io.Reader
|
||||||
|
io.Writer
|
||||||
|
}{os.Stdin, os.Stdout}, "")
|
||||||
|
prompt = string(term.Escape.Cyan) + prompt + " " + string(term.Escape.Reset)
|
||||||
|
if isPassword {
|
||||||
|
for {
|
||||||
|
line, _ = term.ReadPassword(prompt)
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
term.SetPrompt(prompt)
|
||||||
|
if line, _ = term.ReadLine(); line == "" {
|
||||||
|
line = defaultValue
|
||||||
|
}
|
||||||
|
return line
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/influxdata/platform"
|
"github.com/influxdata/platform"
|
||||||
|
@ -17,18 +17,25 @@ type SetupHandler struct {
|
||||||
OnboardingService platform.OnboardingService
|
OnboardingService platform.OnboardingService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
setupPath = "/api/v2/setup"
|
||||||
|
)
|
||||||
|
|
||||||
// NewSetupHandler returns a new instance of SetupHandler.
|
// NewSetupHandler returns a new instance of SetupHandler.
|
||||||
func NewSetupHandler() *SetupHandler {
|
func NewSetupHandler() *SetupHandler {
|
||||||
h := &SetupHandler{
|
h := &SetupHandler{
|
||||||
Router: httprouter.New(),
|
Router: httprouter.New(),
|
||||||
}
|
}
|
||||||
h.HandlerFunc("POST", "/api/v2/setup", h.handlePostSetup)
|
h.HandlerFunc("POST", setupPath, h.handlePostSetup)
|
||||||
h.HandlerFunc("GET", "/api/v2/setup", h.isOnboarding)
|
h.HandlerFunc("GET", setupPath, h.isOnboarding)
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
// isOnboarding is the HTTP handler for the GET /setup route.
|
type isOnboardingResponse struct {
|
||||||
// returns true/false
|
Allowed bool `json:"allowed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// isOnboarding is the HTTP handler for the GET /api/v2/setup route.
|
||||||
func (h *SetupHandler) isOnboarding(w http.ResponseWriter, r *http.Request) {
|
func (h *SetupHandler) isOnboarding(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
result, err := h.OnboardingService.IsOnboarding(ctx)
|
result, err := h.OnboardingService.IsOnboarding(ctx)
|
||||||
|
@ -36,10 +43,13 @@ func (h *SetupHandler) isOnboarding(w http.ResponseWriter, r *http.Request) {
|
||||||
EncodeError(ctx, err, w)
|
EncodeError(ctx, err, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusOK)
|
if err := encodeResponse(ctx, w, http.StatusOK, isOnboardingResponse{result}); err != nil {
|
||||||
fmt.Fprintf(w, `{"allowed": %v}`, result)
|
EncodeError(ctx, err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isOnboarding is the HTTP handler for the POST /api/v2/setup route.
|
||||||
func (h *SetupHandler) handlePostSetup(w http.ResponseWriter, r *http.Request) {
|
func (h *SetupHandler) handlePostSetup(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
||||||
|
@ -83,3 +93,78 @@ func decodePostSetupRequest(ctx context.Context, r *http.Request) (*platform.Onb
|
||||||
|
|
||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetupService connects to Influx via HTTP to perform onboarding operations
|
||||||
|
type SetupService struct {
|
||||||
|
Addr string
|
||||||
|
InsecureSkipVerify bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOnboarding determine if onboarding request is allowed.
|
||||||
|
func (s *SetupService) IsOnboarding(ctx context.Context) (bool, error) {
|
||||||
|
u, err := newURL(s.Addr, setupPath)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest("GET", u.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
hc := newClient(u.Scheme, s.InsecureSkipVerify)
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if err := CheckError(resp); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
var ir isOnboardingResponse
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&ir); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return ir.Allowed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate OnboardingResults.
|
||||||
|
func (s *SetupService) Generate(ctx context.Context, or *platform.OnboardingRequest) (*platform.OnboardingResults, error) {
|
||||||
|
u, err := newURL(s.Addr, setupPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
octets, err := json.Marshal(or)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest("POST", u.String(), bytes.NewReader(octets))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
hc := newClient(u.Scheme, s.InsecureSkipVerify)
|
||||||
|
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// TODO(jsternberg): Should this check for a 201 explicitly?
|
||||||
|
if err := CheckError(resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var oResp onboardingResponse
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&oResp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &platform.OnboardingResults{
|
||||||
|
User: &oResp.User.User,
|
||||||
|
Auth: &oResp.Auth.Authorization,
|
||||||
|
Org: &oResp.Organization.Organization,
|
||||||
|
Bucket: &platform.Bucket{
|
||||||
|
ID: oResp.Bucket.ID,
|
||||||
|
Name: oResp.Bucket.Name,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue