graphql-engine/cli/global_config.go

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

238 lines
7.1 KiB
Go
Raw Permalink Normal View History

package cli
import (
"encoding/json"
stderrors "errors"
"fmt"
"io/fs"
"io/ioutil"
"os"
"path/filepath"
"github.com/gofrs/uuid"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/viper"
"github.com/hasura/graphql-engine/cli/v2/internal/errors"
)
// 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"
)
// 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"`
// CLIEnvironment defines the environment the CLI is running
CLIEnvironment Environment `json:"cli_environment"`
}
type rawGlobalConfig struct {
UUID *string `json:"uuid"`
EnableTelemetry *bool `json:"enable_telemetry"`
ShowUpdateNotification *bool `json:"show_update_notification"`
CLIEnvironment Environment `json:"cli_environment"`
shoudlWrite bool
}
func (c *rawGlobalConfig) read(filename string) error {
var op errors.Op = "cli.rawGlobalConfig.read"
b, err := ioutil.ReadFile(filename)
if err != nil {
return errors.E(op, fmt.Errorf("read file: %w", err))
}
err = json.Unmarshal(b, c)
if err != nil {
return errors.E(op, fmt.Errorf("parse file %w", err))
}
return nil
}
func (c *rawGlobalConfig) validateKeys() error {
var op errors.Op = "cli.rawGlobalConfig.validateKeys"
// check prescence of uuid, create if doesn't exist
if c.UUID == nil {
u, err := uuid.NewV4()
if err != nil {
return errors.E(op, fmt.Errorf("failed generating uuid : %w", err))
}
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
}
if c.CLIEnvironment == "" {
c.CLIEnvironment = DefaultEnvironment
}
return nil
}
func (c *rawGlobalConfig) write(filename string) error {
var op errors.Op = "cli.rawGlobalConfig.write"
b, err := json.MarshalIndent(c, "", " ")
if err != nil {
return errors.E(op, fmt.Errorf("marshal file: %w", err))
}
err = ioutil.WriteFile(filename, b, 0644)
if err != nil {
return errors.E(op, fmt.Errorf("write file: %w", err))
}
return nil
}
// setupGlobConfig ensures that global config directory and file exists and
// reads it into the GlobalConfig object.
func (ec *ExecutionContext) setupGlobalConfig() error {
var op errors.Op = "cli.ExecutionContext.setupGlobalConfig"
// 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 {
return errors.E(op, fmt.Errorf("cannot get home directory: %w", err))
}
globalConfigDir := filepath.Join(home, GlobalConfigDirName)
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 {
return errors.E(op, fmt.Errorf("cannot create global config directory: %w", err))
}
// check if the filename is set, else default
if len(ec.GlobalConfigFile) == 0 {
ec.GlobalConfigFile = filepath.Join(ec.GlobalConfigDir, GlobalConfigFileName)
ec.Logger.Debugf("global config file set as '%s'", ec.GlobalConfigFile)
}
// check if the global config file exist
_, err = os.Stat(ec.GlobalConfigFile)
if stderrors.Is(err, fs.ErrNotExist) {
// 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 {
return errors.E(op, fmt.Errorf("setup global config object: %w", err))
}
// write the file
err = gc.write(ec.GlobalConfigFile)
if err != nil {
return errors.E(op, fmt.Errorf("write global config file: %w", err))
}
ec.Logger.Debugf("global config file written at '%s' with content '%v'", ec.GlobalConfigFile, gc)
// also show a notice about telemetry
ec.Logger.Info(TelemetryNotice)
} else if stderrors.Is(err, fs.ErrExist) || err == nil {
// file exists, verify contents
ec.Logger.Debug("global config file exists, verifying contents")
// initialize the config object
gc := rawGlobalConfig{}
err := gc.read(ec.GlobalConfigFile)
if err != nil {
return errors.E(op, fmt.Errorf("reading global config file failed: %w", err))
}
// validate keys
err = gc.validateKeys()
if err != nil {
return errors.E(op, fmt.Errorf("validating global config file failed: %w", err))
}
// write the file if there are any changes
if gc.shoudlWrite {
err := gc.write(ec.GlobalConfigFile)
if err != nil {
return errors.E(op, fmt.Errorf("writing global config file failed: %w", err))
}
ec.Logger.Debugf("global config file written at '%s' with content '%+#v'", ec.GlobalConfigFile, gc)
}
}
err = ec.readGlobalConfig()
if err != nil {
return errors.E(op, err)
}
return nil
}
// readGlobalConfig reads the configuration from global config file env vars,
// through viper.
func (ec *ExecutionContext) readGlobalConfig() error {
var op errors.Op = "cli.ExecutionContext.readGlobalConfig"
// 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)
v.SetDefault("cli_environment", DefaultEnvironment)
err := v.ReadInConfig()
if err != nil {
return errors.E(op, fmt.Errorf("cannot read global config from file/env: %w", err))
}
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"),
CLIEnvironment: Environment(v.GetString("cli_environment")),
}
} 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)
ec.Logger.Debugf("global config: cliEnvironment: %v", ec.GlobalConfig.CLIEnvironment)
// set if telemetry can be beamed or not
ec.Telemetry.CanBeam = ec.GlobalConfig.EnableTelemetry
ec.Telemetry.UUID = ec.GlobalConfig.UUID
return nil
}