add setup service

pull/10616/head
Kelvin Wang 2018-10-02 13:21:36 -04:00
parent 96129d717f
commit 3addadc12a
3 changed files with 190 additions and 7 deletions

View File

@ -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.

97
cmd/influx/setup.go Normal file
View File

@ -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
}

View File

@ -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
}