cli: add verbose debug logging (#809)

* feat: add debug logging for init command
* feat: add debug logging to recover command
* feat: add debug logging for configfetchmeasurements
* feat: add debug logging for config generate
* feat: added debug logging for miniup command
* feat: add debug logging for upgrade command
* feat: add debug logging for create command
This commit is contained in:
Alex Darby 2023-01-04 09:46:29 +00:00 committed by GitHub
parent baa1b37681
commit 97c72f5f32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 285 additions and 100 deletions

View file

@ -53,8 +53,17 @@ func NewInitCmd() *cobra.Command {
return cmd
}
type initCmd struct {
log debugLog
}
// runInitialize runs the initialize command.
func runInitialize(cmd *cobra.Command, args []string) error {
log, err := newCLILogger(cmd)
if err != nil {
return fmt.Errorf("creating logger: %w", err)
}
defer log.Sync()
fileHandler := file.NewHandler(afero.NewOsFs())
newDialer := func(validator *cloudcmd.Validator) *dialer.Dialer {
return dialer.New(nil, validator.V(cmd), &net.Dialer{})
@ -66,24 +75,26 @@ func runInitialize(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(cmd.Context(), time.Hour)
defer cancel()
cmd.SetContext(ctx)
return initialize(cmd, newDialer, fileHandler, license.NewClient(), spinner)
i := &initCmd{log: log}
return i.initialize(cmd, newDialer, fileHandler, license.NewClient(), spinner)
}
// initialize initializes a Constellation.
func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator) *dialer.Dialer,
func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator) *dialer.Dialer,
fileHandler file.Handler, quotaChecker license.QuotaChecker, spinner spinnerInterf,
) error {
flags, err := evalFlagArgs(cmd)
flags, err := i.evalFlagArgs(cmd)
if err != nil {
return err
}
i.log.Debugf("Using flags: %+v", flags)
i.log.Debugf("Loading config file from %s", flags.configPath)
conf, err := config.New(fileHandler, flags.configPath)
if err != nil {
return displayConfigValidationErrors(cmd.ErrOrStderr(), err)
}
i.log.Debugf("Checking cluster ID file")
var idFile clusterid.File
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil {
return fmt.Errorf("reading cluster ID file: %w", err)
@ -93,32 +104,37 @@ func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator
if err != nil {
return fmt.Errorf("validating kubernetes version: %w", err)
}
i.log.Debugf("Validated k8s version as %s", k8sVersion)
if versions.IsPreviewK8sVersion(k8sVersion) {
cmd.PrintErrf("Warning: Constellation with Kubernetes %v is still in preview. Use only for evaluation purposes.\n", k8sVersion)
}
provider := conf.GetProvider()
i.log.Debugf("Got provider %s", provider.String())
checker := license.NewChecker(quotaChecker, fileHandler)
if err := checker.CheckLicense(cmd.Context(), provider, conf.Provider, cmd.Printf); err != nil {
cmd.PrintErrf("License check failed: %v", err)
}
i.log.Debugf("Checked license")
validator, err := cloudcmd.NewValidator(provider, conf)
if err != nil {
return err
}
serviceAccURI, err := getMarshaledServiceAccountURI(provider, conf, fileHandler)
i.log.Debugf("Created a new validator")
serviceAccURI, err := i.getMarshaledServiceAccountURI(provider, conf, fileHandler)
if err != nil {
return err
}
masterSecret, err := readOrGenerateMasterSecret(cmd.OutOrStdout(), fileHandler, flags.masterSecretPath)
i.log.Debugf("Got service account uri %s", serviceAccURI)
i.log.Debugf("Loading master secret file from %s", flags.masterSecretPath)
masterSecret, err := i.readOrGenerateMasterSecret(cmd.OutOrStdout(), fileHandler, flags.masterSecretPath)
if err != nil {
return fmt.Errorf("parsing or generating master secret from file %s: %w", flags.masterSecretPath, err)
}
helmLoader := helm.NewLoader(provider, k8sVersion)
i.log.Debugf("Created new helm loader")
helmDeployments, err := helmLoader.Load(conf, flags.conformance, masterSecret.Key, masterSecret.Salt)
i.log.Debugf("Loaded helm heployments")
if err != nil {
return fmt.Errorf("loading Helm charts: %w", err)
}
@ -140,7 +156,9 @@ func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator
ConformanceMode: flags.conformance,
InitSecret: idFile.InitSecret,
}
resp, err := initCall(cmd.Context(), newDialer(validator), idFile.IP, req)
i.log.Debugf("Sending initialization request")
resp, err := i.initCall(cmd.Context(), newDialer(validator), idFile.IP, req)
i.log.Debugf("Got initialization response")
spinner.Stop()
if err != nil {
var nonRetriable *nonRetriableError
@ -150,21 +168,23 @@ func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator
}
return err
}
i.log.Debugf("Writing Constellation id file")
idFile.CloudProvider = provider
if err := writeOutput(idFile, resp, cmd.OutOrStdout(), fileHandler); err != nil {
if err := i.writeOutput(idFile, resp, cmd.OutOrStdout(), fileHandler); err != nil {
return err
}
return nil
}
func initCall(ctx context.Context, dialer grpcDialer, ip string, req *initproto.InitRequest) (*initproto.InitResponse, error) {
func (i *initCmd) initCall(ctx context.Context, dialer grpcDialer, ip string, req *initproto.InitRequest) (*initproto.InitResponse, error) {
doer := &initDoer{
dialer: dialer,
endpoint: net.JoinHostPort(ip, strconv.Itoa(constants.BootstrapperPort)),
req: req,
log: i.log,
}
i.log.Debugf("Making initialization call, doer is %+v", doer)
retrier := retry.NewIntervalRetrier(doer, 30*time.Second, grpcRetry.ServiceIsUnavailable)
if err := retrier.Do(ctx); err != nil {
return nil, err
@ -177,15 +197,18 @@ type initDoer struct {
endpoint string
req *initproto.InitRequest
resp *initproto.InitResponse
log debugLog
}
func (d *initDoer) Do(ctx context.Context) error {
conn, err := d.dialer.Dial(ctx, d.endpoint)
if err != nil {
d.log.Debugf("Dialing init server failed: %w. Retrying...", err)
return fmt.Errorf("dialing init server: %w", err)
}
defer conn.Close()
protoClient := initproto.NewAPIClient(conn)
d.log.Debugf("Created protoClient")
resp, err := protoClient.Init(ctx, d.req)
if err != nil {
return &nonRetriableError{fmt.Errorf("init call: %w", err)}
@ -194,10 +217,11 @@ func (d *initDoer) Do(ctx context.Context) error {
return nil
}
func writeOutput(idFile clusterid.File, resp *initproto.InitResponse, wr io.Writer, fileHandler file.Handler) error {
func (i *initCmd) writeOutput(idFile clusterid.File, resp *initproto.InitResponse, wr io.Writer, fileHandler file.Handler) error {
fmt.Fprint(wr, "Your Constellation cluster was successfully initialized.\n\n")
ownerID := hex.EncodeToString(resp.OwnerId)
i.log.Debugf("Owner id is %s", ownerID)
clusterID := hex.EncodeToString(resp.ClusterId)
tw := tabwriter.NewWriter(wr, 0, 0, 2, ' ', 0)
@ -210,13 +234,14 @@ func writeOutput(idFile clusterid.File, resp *initproto.InitResponse, wr io.Writ
if err := fileHandler.Write(constants.AdminConfFilename, resp.Kubeconfig, file.OptNone); err != nil {
return fmt.Errorf("writing kubeconfig: %w", err)
}
i.log.Debugf("Wrote out kubeconfig")
idFile.OwnerID = ownerID
idFile.ClusterID = clusterID
if err := fileHandler.WriteJSON(constants.ClusterIDsFileName, idFile, file.OptOverwrite); err != nil {
return fmt.Errorf("writing Constellation id file: %w", err)
}
i.log.Debugf("Wrote out Constellation id file")
fmt.Fprintln(wr, "You can now connect to your cluster by executing:")
fmt.Fprintf(wr, "\texport KUBECONFIG=\"$PWD/%s\"\n", constants.AdminConfFilename)
@ -229,16 +254,19 @@ func writeRow(wr io.Writer, col1 string, col2 string) {
// evalFlagArgs gets the flag values and does preprocessing of these values like
// reading the content from file path flags and deriving other values from flag combinations.
func evalFlagArgs(cmd *cobra.Command) (initFlags, error) {
func (i *initCmd) evalFlagArgs(cmd *cobra.Command) (initFlags, error) {
masterSecretPath, err := cmd.Flags().GetString("master-secret")
i.log.Debugf("Master secret path flag value is %s", masterSecretPath)
if err != nil {
return initFlags{}, fmt.Errorf("parsing master-secret path flag: %w", err)
}
conformance, err := cmd.Flags().GetBool("conformance")
i.log.Debugf("Conformance flag is %t", conformance)
if err != nil {
return initFlags{}, fmt.Errorf("parsing autoscale flag: %w", err)
}
configPath, err := cmd.Flags().GetString("config")
i.log.Debugf("Config path flag is %s", conformance)
if err != nil {
return initFlags{}, fmt.Errorf("parsing config path flag: %w", err)
}
@ -264,8 +292,9 @@ type masterSecret struct {
}
// readOrGenerateMasterSecret reads a base64 encoded master secret from file or generates a new 32 byte secret.
func readOrGenerateMasterSecret(outWriter io.Writer, fileHandler file.Handler, filename string) (masterSecret, error) {
func (i *initCmd) readOrGenerateMasterSecret(outWriter io.Writer, fileHandler file.Handler, filename string) (masterSecret, error) {
if filename != "" {
i.log.Debugf("Reading master secret from file")
var secret masterSecret
if err := fileHandler.ReadJSON(filename, &secret); err != nil {
return masterSecret{}, err
@ -281,6 +310,7 @@ func readOrGenerateMasterSecret(outWriter io.Writer, fileHandler file.Handler, f
}
// No file given, generate a new secret, and save it to disk
i.log.Debugf("Generating new master secret")
key, err := crypto.GenerateRandomBytes(crypto.MasterSecretLengthDefault)
if err != nil {
return masterSecret{}, err
@ -293,7 +323,7 @@ func readOrGenerateMasterSecret(outWriter io.Writer, fileHandler file.Handler, f
Key: key,
Salt: salt,
}
i.log.Debugf("Generated master secret key and salt values")
if err := fileHandler.WriteJSON(constants.MasterSecretFilename, secret, file.OptNone); err != nil {
return masterSecret{}, err
}
@ -312,21 +342,26 @@ func readIPFromIDFile(fileHandler file.Handler) (string, error) {
return idFile.IP, nil
}
func getMarshaledServiceAccountURI(provider cloudprovider.Provider, config *config.Config, fileHandler file.Handler) (string, error) {
func (i *initCmd) getMarshaledServiceAccountURI(provider cloudprovider.Provider, config *config.Config, fileHandler file.Handler) (string, error) {
i.log.Debugf("Getting service account URI")
switch provider {
case cloudprovider.GCP:
i.log.Debugf("Handling case for GCP")
path := config.Provider.GCP.ServiceAccountKeyPath
i.log.Debugf("GCP service account key path %s", path)
var key gcpshared.ServiceAccountKey
if err := fileHandler.ReadJSON(path, &key); err != nil {
return "", fmt.Errorf("reading service account key from path %q: %w", path, err)
}
i.log.Debugf("Read GCP service account key from path")
return key.ToCloudServiceAccountURI(), nil
case cloudprovider.AWS:
i.log.Debugf("Handling case for AWS")
return "", nil // AWS does not need a service account URI
case cloudprovider.Azure:
i.log.Debugf("Handling case for Azure")
creds := azureshared.ApplicationCredentials{
TenantID: config.Provider.Azure.TenantID,
AppClientID: config.Provider.Azure.AppClientID,
@ -336,6 +371,7 @@ func getMarshaledServiceAccountURI(provider cloudprovider.Provider, config *conf
return creds.ToCloudServiceAccountURI(), nil
case cloudprovider.QEMU:
i.log.Debugf("Handling case for QEMU")
return "", nil // QEMU does not use service account keys
default: