2019-02-04 13:51:29 +03:00
|
|
|
package cli
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2022-11-28 11:44:18 +03:00
|
|
|
stderrors "errors"
|
2021-10-13 17:38:07 +03:00
|
|
|
"fmt"
|
2022-11-28 11:44:18 +03:00
|
|
|
"io/fs"
|
2019-02-04 13:51:29 +03:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/gofrs/uuid"
|
|
|
|
homedir "github.com/mitchellh/go-homedir"
|
|
|
|
"github.com/spf13/viper"
|
2022-11-03 11:07:50 +03:00
|
|
|
|
|
|
|
"github.com/hasura/graphql-engine/cli/v2/internal/errors"
|
2019-02-04 13:51:29 +03:00
|
|
|
)
|
|
|
|
|
2020-04-22 12:15:42 +03:00
|
|
|
// Environment defines the environment the CLI is running
|
|
|
|
type Environment string
|
|
|
|
|
|
|
|
const (
|
|
|
|
// DefaultEnvironment - CLI running in default mode
|
|
|
|
DefaultEnvironment Environment = "default"
|
|
|
|
// ServerOnDockerEnvironment - CLI running in cli-migrations image
|
|
|
|
ServerOnDockerEnvironment = "server-on-docker"
|
|
|
|
)
|
|
|
|
|
2019-02-04 13:51:29 +03:00
|
|
|
// GlobalConfig is the configuration object stored in the GlobalConfigFile.
|
|
|
|
type GlobalConfig struct {
|
|
|
|
// UUID used for telemetry, generated on first run.
|
|
|
|
UUID string `json:"uuid"`
|
|
|
|
|
|
|
|
// Indicate if telemetry is enabled or not
|
|
|
|
EnableTelemetry bool `json:"enable_telemetry"`
|
|
|
|
|
|
|
|
// Indicates whether update notifications should be shown or not
|
|
|
|
ShowUpdateNotification bool `json:"show_update_notification"`
|
2020-04-22 12:15:42 +03:00
|
|
|
|
|
|
|
// CLIEnvironment defines the environment the CLI is running
|
|
|
|
CLIEnvironment Environment `json:"cli_environment"`
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
type rawGlobalConfig struct {
|
2020-04-22 12:15:42 +03:00
|
|
|
UUID *string `json:"uuid"`
|
|
|
|
EnableTelemetry *bool `json:"enable_telemetry"`
|
|
|
|
ShowUpdateNotification *bool `json:"show_update_notification"`
|
|
|
|
CLIEnvironment Environment `json:"cli_environment"`
|
2019-02-04 13:51:29 +03:00
|
|
|
|
|
|
|
shoudlWrite bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *rawGlobalConfig) read(filename string) error {
|
2022-11-03 11:07:50 +03:00
|
|
|
var op errors.Op = "cli.rawGlobalConfig.read"
|
2019-02-04 13:51:29 +03:00
|
|
|
b, err := ioutil.ReadFile(filename)
|
|
|
|
if err != nil {
|
2022-11-03 11:07:50 +03:00
|
|
|
return errors.E(op, fmt.Errorf("read file: %w", err))
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
|
|
|
err = json.Unmarshal(b, c)
|
|
|
|
if err != nil {
|
2022-11-03 11:07:50 +03:00
|
|
|
return errors.E(op, fmt.Errorf("parse file %w", err))
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *rawGlobalConfig) validateKeys() error {
|
2022-11-03 11:07:50 +03:00
|
|
|
var op errors.Op = "cli.rawGlobalConfig.validateKeys"
|
2019-02-04 13:51:29 +03:00
|
|
|
// check prescence of uuid, create if doesn't exist
|
|
|
|
if c.UUID == nil {
|
|
|
|
u, err := uuid.NewV4()
|
|
|
|
if err != nil {
|
2022-11-03 11:07:50 +03:00
|
|
|
return errors.E(op, fmt.Errorf("failed generating uuid : %w", err))
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
|
|
|
uid := u.String()
|
|
|
|
c.UUID = &uid
|
|
|
|
c.shoudlWrite = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// check enabletelemetry
|
|
|
|
if c.EnableTelemetry == nil {
|
|
|
|
trueVal := true
|
|
|
|
c.EnableTelemetry = &trueVal
|
|
|
|
c.shoudlWrite = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// check showupdatenotification
|
|
|
|
if c.ShowUpdateNotification == nil {
|
|
|
|
trueVal := true
|
|
|
|
c.ShowUpdateNotification = &trueVal
|
|
|
|
c.shoudlWrite = true
|
|
|
|
}
|
|
|
|
|
2020-04-22 12:15:42 +03:00
|
|
|
if c.CLIEnvironment == "" {
|
|
|
|
c.CLIEnvironment = DefaultEnvironment
|
|
|
|
}
|
|
|
|
|
2019-02-04 13:51:29 +03:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *rawGlobalConfig) write(filename string) error {
|
2022-11-03 11:07:50 +03:00
|
|
|
var op errors.Op = "cli.rawGlobalConfig.write"
|
2019-02-04 13:51:29 +03:00
|
|
|
b, err := json.MarshalIndent(c, "", " ")
|
|
|
|
if err != nil {
|
2022-11-03 11:07:50 +03:00
|
|
|
return errors.E(op, fmt.Errorf("marshal file: %w", err))
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
|
|
|
err = ioutil.WriteFile(filename, b, 0644)
|
|
|
|
if err != nil {
|
2022-11-03 11:07:50 +03:00
|
|
|
return errors.E(op, fmt.Errorf("write file: %w", err))
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// setupGlobConfig ensures that global config directory and file exists and
|
|
|
|
// reads it into the GlobalConfig object.
|
|
|
|
func (ec *ExecutionContext) setupGlobalConfig() error {
|
2022-11-03 11:07:50 +03:00
|
|
|
var op errors.Op = "cli.ExecutionContext.setupGlobalConfig"
|
2019-02-04 13:51:29 +03:00
|
|
|
// check if the directory name is set, else default
|
|
|
|
if len(ec.GlobalConfigDir) == 0 {
|
|
|
|
ec.Logger.Debug("global config directory is not pre-set, defaulting")
|
|
|
|
home, err := homedir.Dir()
|
|
|
|
if err != nil {
|
2022-11-03 11:07:50 +03:00
|
|
|
return errors.E(op, fmt.Errorf("cannot get home directory: %w", err))
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
2019-02-14 12:37:47 +03:00
|
|
|
globalConfigDir := filepath.Join(home, GlobalConfigDirName)
|
2019-02-04 13:51:29 +03:00
|
|
|
ec.GlobalConfigDir = globalConfigDir
|
|
|
|
ec.Logger.Debugf("global config directory set as '%s'", ec.GlobalConfigDir)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create the config directory
|
|
|
|
err := os.MkdirAll(ec.GlobalConfigDir, os.ModePerm)
|
|
|
|
if err != nil {
|
2022-11-03 11:07:50 +03:00
|
|
|
return errors.E(op, fmt.Errorf("cannot create global config directory: %w", err))
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// check if the filename is set, else default
|
|
|
|
if len(ec.GlobalConfigFile) == 0 {
|
2019-02-14 12:37:47 +03:00
|
|
|
ec.GlobalConfigFile = filepath.Join(ec.GlobalConfigDir, GlobalConfigFileName)
|
2019-02-04 13:51:29 +03:00
|
|
|
ec.Logger.Debugf("global config file set as '%s'", ec.GlobalConfigFile)
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if the global config file exist
|
|
|
|
_, err = os.Stat(ec.GlobalConfigFile)
|
2022-11-28 11:44:18 +03:00
|
|
|
if stderrors.Is(err, fs.ErrNotExist) {
|
2019-02-04 13:51:29 +03:00
|
|
|
|
|
|
|
// file does not exist, teat as first run and create it
|
|
|
|
ec.Logger.Debug("global config file does not exist, this could be the first run, creating it...")
|
|
|
|
|
|
|
|
// create an empty config object
|
|
|
|
gc := &rawGlobalConfig{}
|
|
|
|
|
|
|
|
// populate the keys
|
|
|
|
err := gc.validateKeys()
|
|
|
|
if err != nil {
|
2022-11-03 11:07:50 +03:00
|
|
|
return errors.E(op, fmt.Errorf("setup global config object: %w", err))
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// write the file
|
|
|
|
err = gc.write(ec.GlobalConfigFile)
|
|
|
|
if err != nil {
|
2022-11-03 11:07:50 +03:00
|
|
|
return errors.E(op, fmt.Errorf("write global config file: %w", err))
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
|
|
|
ec.Logger.Debugf("global config file written at '%s' with content '%v'", ec.GlobalConfigFile, gc)
|
|
|
|
|
|
|
|
// also show a notice about telemetry
|
2021-09-29 14:11:24 +03:00
|
|
|
ec.Logger.Info(TelemetryNotice)
|
2019-02-04 13:51:29 +03:00
|
|
|
|
2022-11-28 11:44:18 +03:00
|
|
|
} else if stderrors.Is(err, fs.ErrExist) || err == nil {
|
2019-02-04 13:51:29 +03:00
|
|
|
|
|
|
|
// file exists, verify contents
|
2021-05-11 09:47:24 +03:00
|
|
|
ec.Logger.Debug("global config file exists, verifying contents")
|
2019-02-04 13:51:29 +03:00
|
|
|
|
|
|
|
// initialize the config object
|
|
|
|
gc := rawGlobalConfig{}
|
|
|
|
err := gc.read(ec.GlobalConfigFile)
|
|
|
|
if err != nil {
|
2022-11-03 11:07:50 +03:00
|
|
|
return errors.E(op, fmt.Errorf("reading global config file failed: %w", err))
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// validate keys
|
|
|
|
err = gc.validateKeys()
|
|
|
|
if err != nil {
|
2022-11-03 11:07:50 +03:00
|
|
|
return errors.E(op, fmt.Errorf("validating global config file failed: %w", err))
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// write the file if there are any changes
|
|
|
|
if gc.shoudlWrite {
|
|
|
|
err := gc.write(ec.GlobalConfigFile)
|
|
|
|
if err != nil {
|
2022-11-03 11:07:50 +03:00
|
|
|
return errors.E(op, fmt.Errorf("writing global config file failed: %w", err))
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
|
|
|
ec.Logger.Debugf("global config file written at '%s' with content '%+#v'", ec.GlobalConfigFile, gc)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2022-11-03 11:07:50 +03:00
|
|
|
err = ec.readGlobalConfig()
|
|
|
|
if err != nil {
|
2023-01-16 12:40:58 +03:00
|
|
|
return errors.E(op, err)
|
2022-11-03 11:07:50 +03:00
|
|
|
}
|
|
|
|
return nil
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// readGlobalConfig reads the configuration from global config file env vars,
|
|
|
|
// through viper.
|
|
|
|
func (ec *ExecutionContext) readGlobalConfig() error {
|
2022-11-03 11:07:50 +03:00
|
|
|
var op errors.Op = "cli.ExecutionContext.readGlobalConfig"
|
2019-02-04 13:51:29 +03:00
|
|
|
// need to get existing viper because https://github.com/spf13/viper/issues/233
|
|
|
|
v := viper.New()
|
|
|
|
v.SetEnvPrefix("HASURA_GRAPHQL")
|
|
|
|
v.AutomaticEnv()
|
|
|
|
v.SetConfigName("config")
|
|
|
|
v.AddConfigPath(ec.GlobalConfigDir)
|
2020-04-22 12:15:42 +03:00
|
|
|
v.SetDefault("cli_environment", DefaultEnvironment)
|
2019-02-04 13:51:29 +03:00
|
|
|
err := v.ReadInConfig()
|
|
|
|
if err != nil {
|
2022-11-03 11:07:50 +03:00
|
|
|
return errors.E(op, fmt.Errorf("cannot read global config from file/env: %w", err))
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
|
|
|
if ec.GlobalConfig == nil {
|
|
|
|
ec.Logger.Debugf("global config is not pre-set, reading from current env")
|
|
|
|
ec.GlobalConfig = &GlobalConfig{
|
|
|
|
UUID: v.GetString("uuid"),
|
|
|
|
EnableTelemetry: v.GetBool("enable_telemetry"),
|
|
|
|
ShowUpdateNotification: v.GetBool("show_update_notification"),
|
2020-04-22 12:15:42 +03:00
|
|
|
CLIEnvironment: Environment(v.GetString("cli_environment")),
|
2019-02-04 13:51:29 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ec.Logger.Debugf("global config is pre-set to %#v", ec.GlobalConfig)
|
|
|
|
}
|
|
|
|
ec.Logger.Debugf("global config: uuid: %v", ec.GlobalConfig.UUID)
|
|
|
|
ec.Logger.Debugf("global config: enableTelemetry: %v", ec.GlobalConfig.EnableTelemetry)
|
|
|
|
ec.Logger.Debugf("global config: showUpdateNotification: %v", ec.GlobalConfig.ShowUpdateNotification)
|
2020-04-22 12:15:42 +03:00
|
|
|
ec.Logger.Debugf("global config: cliEnvironment: %v", ec.GlobalConfig.CLIEnvironment)
|
2019-02-04 13:51:29 +03:00
|
|
|
|
|
|
|
// set if telemetry can be beamed or not
|
|
|
|
ec.Telemetry.CanBeam = ec.GlobalConfig.EnableTelemetry
|
|
|
|
ec.Telemetry.UUID = ec.GlobalConfig.UUID
|
|
|
|
return nil
|
|
|
|
}
|