205 строки
5.3 KiB
Go
205 строки
5.3 KiB
Go
// Copyright (c) Microsoft and contributors. All rights reserved.
|
|
//
|
|
// This source code is licensed under the MIT license found in the
|
|
// LICENSE file in the root directory of this source tree.
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/google/subcommands"
|
|
)
|
|
|
|
func buildLicenseURL(host string, port int) string {
|
|
return fmt.Sprintf("http://%s:%d/_xpack/license", host, port)
|
|
}
|
|
|
|
func buildHTTPClient() *http.Client {
|
|
return &http.Client{
|
|
Timeout: time.Minute * 1,
|
|
}
|
|
}
|
|
|
|
func loadBasicAuth(authFile string) (*basicAuth, error) {
|
|
file, err := ioutil.ReadFile(authFile) // #nosec
|
|
if err != nil {
|
|
return nil, fmt.Errorf(`Failed to read the basic authentication
|
|
credentials from the file: %v`, err)
|
|
}
|
|
|
|
var auth basicAuth
|
|
err = json.Unmarshal(file, &auth)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to unmarshal the basic auth: %v", err)
|
|
}
|
|
return &auth, nil
|
|
}
|
|
|
|
func setBasicAuth(req *http.Request, authFile string) error {
|
|
if authFile != "" {
|
|
basicAuth, err := loadBasicAuth(authFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.SetBasicAuth(basicAuth.Username, basicAuth.Password)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type basicAuth struct {
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
type viewCmd struct {
|
|
host string
|
|
port int
|
|
authFile string
|
|
}
|
|
|
|
func (*viewCmd) Name() string { return "view" }
|
|
func (*viewCmd) Synopsis() string { return "Display the installed license in Elasticsearch" }
|
|
func (*viewCmd) Usage() string {
|
|
return `view [-host] <host name> [-port] <port> [-auth-file] <basic auth file path>:
|
|
Display the installed license to stdout
|
|
`
|
|
}
|
|
|
|
func (v *viewCmd) SetFlags(f *flag.FlagSet) {
|
|
f.StringVar(&v.host, "host", "localhost", "Host name of the Elasticsearch API")
|
|
f.IntVar(&v.port, "port", 9200, "Port of the Elastisearch API")
|
|
f.StringVar(&v.authFile, "auth-file", "", "File with basic authentication credentials")
|
|
}
|
|
|
|
func (v *viewCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
|
client := buildHTTPClient()
|
|
licenseURL := buildLicenseURL(v.host, v.port)
|
|
|
|
req, err := http.NewRequest(http.MethodGet, licenseURL, nil)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return subcommands.ExitFailure
|
|
}
|
|
|
|
err = setBasicAuth(req, v.authFile)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return subcommands.ExitFailure
|
|
}
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return subcommands.ExitFailure
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
fmt.Printf("Failed to get the license information with status code [%d]",
|
|
resp.StatusCode)
|
|
return subcommands.ExitFailure
|
|
}
|
|
|
|
content, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
fmt.Println("Failed to read the license from the HTTP response body")
|
|
return subcommands.ExitFailure
|
|
}
|
|
|
|
var prettyContent bytes.Buffer
|
|
err = json.Indent(&prettyContent, content, "", " ")
|
|
if err != nil {
|
|
fmt.Printf("Failed to indent the license content: %s\n", string(content))
|
|
return subcommands.ExitFailure
|
|
}
|
|
|
|
fmt.Print(string(prettyContent.Bytes()))
|
|
|
|
return subcommands.ExitSuccess
|
|
}
|
|
|
|
type installCmd struct {
|
|
host string
|
|
port int
|
|
licenseFile string
|
|
authFile string
|
|
}
|
|
|
|
func (*installCmd) Name() string { return "install" }
|
|
func (*installCmd) Synopsis() string { return "Install a new Elasticsearch license" }
|
|
func (*installCmd) Usage() string {
|
|
return `install [-host] <host name> [-port] <port> [-license-file] <path to license file> [-auth-file] <path to basic auth file>
|
|
Install a new license into Elasticsearch cluster
|
|
`
|
|
}
|
|
|
|
func (i *installCmd) SetFlags(f *flag.FlagSet) {
|
|
f.StringVar(&i.host, "host", "localhost", "Host name of the Elasticsearch API")
|
|
f.IntVar(&i.port, "port", 9200, "Port of the Elastisearch API")
|
|
f.StringVar(&i.licenseFile, "license-file", "", "Path to license file")
|
|
f.StringVar(&i.authFile, "auth-file", "", "Path to basic auth file")
|
|
}
|
|
|
|
func (i *installCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
|
if _, err := os.Stat(i.licenseFile); os.IsNotExist(err) {
|
|
fmt.Printf("License file '%s' not found\n", i.licenseFile)
|
|
return subcommands.ExitFailure
|
|
}
|
|
|
|
file, err := os.Open(i.licenseFile)
|
|
if err != nil {
|
|
fmt.Printf("Failed to open the license file '%s'\n", i.licenseFile)
|
|
return subcommands.ExitFailure
|
|
}
|
|
|
|
licenseURL := buildLicenseURL(i.host, i.port)
|
|
req, err := http.NewRequest(http.MethodPut, licenseURL, file)
|
|
if err != nil {
|
|
fmt.Printf("Failed to build the request to install the license: %v\n", err)
|
|
return subcommands.ExitFailure
|
|
}
|
|
req.Header.Set("Content-Type", "application/json")
|
|
err = setBasicAuth(req, i.authFile)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return subcommands.ExitFailure
|
|
}
|
|
|
|
client := buildHTTPClient()
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return subcommands.ExitFailure
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
fmt.Printf("Failed to install the license with status code [%d]\n", resp.StatusCode)
|
|
return subcommands.ExitFailure
|
|
}
|
|
|
|
return subcommands.ExitSuccess
|
|
}
|
|
|
|
func main() {
|
|
subcommands.Register(subcommands.HelpCommand(), "")
|
|
subcommands.Register(subcommands.FlagsCommand(), "")
|
|
subcommands.Register(subcommands.CommandsCommand(), "")
|
|
subcommands.Register(&viewCmd{}, "")
|
|
subcommands.Register(&installCmd{}, "")
|
|
|
|
flag.Parse()
|
|
ctx := context.Background()
|
|
os.Exit(int(subcommands.Execute(ctx)))
|
|
}
|