Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Move code from main.go to other files #164

Merged
merged 4 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions canonical_facts_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
"encoding/json"
"fmt"
"github.com/urfave/cli/v2"
)

// canonicalFactAction tries to gather canonical facts about system,
// and it prints JSON with facts to stdout.
func canonicalFactAction(_ *cli.Context) error {
// NOTE: CLI context is not useful for anything
facts, err := GetCanonicalFacts()
if err != nil {
return cli.Exit(err, 1)
}
data, err := json.MarshalIndent(facts, "", " ")
if err != nil {
return err
}
fmt.Println(string(data))
return nil
}
55 changes: 1 addition & 54 deletions conf.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
package main

import (
"crypto/tls"
"crypto/x509"
"fmt"
"os"
"path/filepath"
)

const (
cliLogLevel = "log-level"
cliCertFile = "cert-file"
Expand All @@ -22,49 +14,4 @@ type Conf struct {
CADir string
}

// Create a TLSConfig using the rhsm certificates
func (conf *Conf) CreateTLSClientConfig() (*tls.Config, error) {
var certData, keyData []byte
var err error
rootCAs := make([][]byte, 0)

KeyDir := conf.KeyFile
CertDir := conf.CertFile

certData, err = os.ReadFile(CertDir)
if err != nil {
return nil, fmt.Errorf("cannot read cert-file: %w", err)
}

keyData, err = os.ReadFile(KeyDir)
if err != nil {
return nil, fmt.Errorf("cannot read key-file: %w", err)
}
CAFiles, err := os.ReadDir(conf.CADir)
if err != nil {
return nil, fmt.Errorf("cannot read ca files: %w", err)
}
for _, file := range CAFiles {
fPath := filepath.Join(conf.CADir, file.Name())
data, err := os.ReadFile(fPath)
if err != nil {
return nil, fmt.Errorf("cannot read ca-file %s : %w", fPath, err)
}
rootCAs = append(rootCAs, data)
}

tlsConfig := &tls.Config{}
cert, err := tls.X509KeyPair(certData, keyData)
if err != nil {
return nil, fmt.Errorf("cannot create key pair: %w", err)
}
tlsConfig.Certificates = []tls.Certificate{cert}

// Create a pool with CAcerts from rhsm CA directory
pool := x509.NewCertPool()
for _, data := range rootCAs {
pool.AppendCertsFromPEM(data)
}

return tlsConfig, nil
}
var config = Conf{}
215 changes: 215 additions & 0 deletions connect_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package main

import (
"encoding/json"
"fmt"
"github.com/subpop/go-log"
"github.com/urfave/cli/v2"
"os"
"time"
)

// ConnectResult is structure holding information about results
// of connect command. The result could be printed in machine-readable format.
type ConnectResult struct {
Hostname string `json:"hostname"`
HostnameError string `json:"hostname_error,omitempty"`
UID int `json:"uid"`
UIDError string `json:"uid_error,omitempty"`
RHSMConnected bool `json:"rhsm_connected"`
RHSMConnectError string `json:"rhsm_connect_error,omitempty"`
InsightsConnected bool `json:"insights_connected"`
InsightsError string `json:"insights_connect_error,omitempty"`
YggdrasilStarted bool `json:"yggdrasil_started"`
YggdrasilStartedError string `json:"yggdrasil_started_error,omitempty"`
format string
}

// Error implement error interface for structure ConnectResult
func (connectResult ConnectResult) Error() string {
var result string
switch connectResult.format {
case "json":
data, err := json.MarshalIndent(connectResult, "", " ")
if err != nil {
return err.Error()
}
result = string(data)
case "":
break
default:
result = "error: unsupported document format: " + connectResult.format
}
return result
}

// beforeConnectAction ensures that user has supplied a correct CLI options
// and there is no conflict between provided options
func beforeConnectAction(ctx *cli.Context) error {
// First check if machine-readable format is used
err := setupFormatOption(ctx)
if err != nil {
return err
}

username := ctx.String("username")
password := ctx.String("password")
organization := ctx.String("organization")
activationKeys := ctx.StringSlice("activation-key")

if len(activationKeys) > 0 {
if username != "" {
return fmt.Errorf("--username and --activation-key can not be used together")
}
if organization == "" {
return fmt.Errorf("--organization is required, when --activation-key is used")
}
}

// When machine-readable format is used, then additional requirements have to be met
if uiSettings.isMachineReadable {
if username == "" || password == "" {
return fmt.Errorf("--username/--password or --organization/--activation-key are required when a machine-readable format is used")
}
}

return checkForUnknownArgs(ctx)
}

// connectAction tries to register system against Red Hat Subscription Management,
// gather the profile information that the system will configure
// connect system to Red Hat Insights, and it also tries to start rhcd service
func connectAction(ctx *cli.Context) error {
var connectResult ConnectResult
connectResult.format = ctx.String("format")

uid := os.Getuid()
if uid != 0 {
errMsg := "non-root user cannot connect system"
exitCode := 1
if uiSettings.isMachineReadable {
connectResult.UID = uid
connectResult.UIDError = errMsg
return cli.Exit(connectResult, exitCode)
} else {
return cli.Exit(fmt.Errorf("error: %s", errMsg), exitCode)
}
}

hostname, err := os.Hostname()
if uiSettings.isMachineReadable {
connectResult.Hostname = hostname
}
if err != nil {
exitCode := 1
if uiSettings.isMachineReadable {
connectResult.HostnameError = err.Error()
return cli.Exit(connectResult, exitCode)
} else {
return cli.Exit(err, exitCode)
}
}

interactivePrintf("Connecting %v to %v.\nThis might take a few seconds.\n\n", hostname, Provider)

var start time.Time
durations := make(map[string]time.Duration)
errorMessages := make(map[string]LogMessage)
/* 1. Register to RHSM, because we need to get consumer certificate. This blocks following action */
start = time.Now()
var returnedMsg string
returnedMsg, err = registerRHSM(ctx)
if err != nil {
connectResult.RHSMConnected = false
errorMessages["rhsm"] = LogMessage{
level: log.LevelError,
message: fmt.Errorf("cannot connect to Red Hat Subscription Management: %w",
err)}
if uiSettings.isMachineReadable {
connectResult.RHSMConnectError = errorMessages["rhsm"].message.Error()
} else {
fmt.Printf(
"%v Cannot connect to Red Hat Subscription Management\n",
uiSettings.iconError,
)
}
} else {
connectResult.RHSMConnected = true
interactivePrintf("%v %v\n", uiSettings.iconOK, returnedMsg)
}
durations["rhsm"] = time.Since(start)

/* 2. Register insights-client */
if errors, exist := errorMessages["rhsm"]; exist {
if errors.level == log.LevelError {
interactivePrintf(
"%v Skipping connection to Red Hat Insights\n",
uiSettings.iconError,
)
}
} else {
start = time.Now()
err = showProgress(" Connecting to Red Hat Insights...", registerInsights)
if err != nil {
connectResult.InsightsConnected = false
errorMessages["insights"] = LogMessage{
level: log.LevelError,
message: fmt.Errorf("cannot connect to Red Hat Insights: %w", err)}
if uiSettings.isMachineReadable {
connectResult.InsightsError = errorMessages["insights"].message.Error()
} else {
fmt.Printf("%v Cannot connect to Red Hat Insights\n", uiSettings.iconError)
}
} else {
connectResult.InsightsConnected = true
interactivePrintf("%v Connected to Red Hat Insights\n", uiSettings.iconOK)
}
durations["insights"] = time.Since(start)
}

/* 3. Start yggdrasil (rhcd) service */
if rhsmErrMsg, exist := errorMessages["rhsm"]; exist && rhsmErrMsg.level == log.LevelError {
connectResult.YggdrasilStarted = false
interactivePrintf(
"%v Skipping activation of %v service\n",
uiSettings.iconError,
ServiceName,
)
} else {
start = time.Now()
progressMessage := fmt.Sprintf(" Activating the %v service", ServiceName)
err = showProgress(progressMessage, activateService)
if err != nil {
connectResult.YggdrasilStarted = false
errorMessages[ServiceName] = LogMessage{
level: log.LevelError,
message: fmt.Errorf("cannot activate %s service: %w",
ServiceName, err)}
if uiSettings.isMachineReadable {
connectResult.YggdrasilStartedError = errorMessages[ServiceName].message.Error()
} else {
fmt.Printf("%v Cannot activate the %v service\n", uiSettings.iconError, ServiceName)
}
} else {
connectResult.YggdrasilStarted = true
interactivePrintf("%v Activated the %v service\n", uiSettings.iconOK, ServiceName)
}
durations[ServiceName] = time.Since(start)
interactivePrintf("\nSuccessfully connected to Red Hat!\n")
}

if !uiSettings.isMachineReadable {
/* 5. Show footer message */
fmt.Printf("\nManage your connected systems: https://red.ht/connector\n")

/* 6. Optionally display duration time of each sub-action */
showTimeDuration(durations)
}

err = showErrorMessages("connect", errorMessages)
if err != nil {
return err
}

return cli.Exit(connectResult, 0)
}
Loading
Loading