Skip to content

Commit

Permalink
refactor(api,cli,ssh,pkg): standardize access to environment variables
Browse files Browse the repository at this point in the history
  • Loading branch information
henrybarreto authored and gustavosbarreto committed Sep 12, 2023
1 parent 171d320 commit 20395db
Show file tree
Hide file tree
Showing 11 changed files with 309 additions and 38 deletions.
32 changes: 24 additions & 8 deletions agent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/kelseyhightower/envconfig"
"github.com/shellhub-io/shellhub/pkg/agent"
"github.com/shellhub-io/shellhub/pkg/agent/pkg/selfupdater"
"github.com/shellhub-io/shellhub/pkg/envs"
"github.com/shellhub-io/shellhub/pkg/loglevel"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand All @@ -31,13 +32,28 @@ func main() {
Run: func(cmd *cobra.Command, args []string) {
loglevel.SetLogLevel()

cfg := agent.Config{}

// Process unprefixed env vars for backward compatibility
envconfig.Process("", &cfg) // nolint:errcheck

if err := envconfig.Process("shellhub", &cfg); err != nil {
// show envconfig usage help users to run agent
// NOTE(r): When T, the generic parameter, is a structure with required tag, the fallback for an
// "unprefixed" parameter is used.
//
// For example,
//
// For the structure below, the parser will parse successfully when the variables exist with or without the
// prefixes since the "required" tag is set to true.
//
// SHELLHUB_TENANT_ID=00000000-0000-4000-0000-000000000000 SERVER_ADDRESS=http://127.0.0.1
// PRIVATE_KEY=/tmp/shellhub sudo -E ./agent
//
// struct {
// ServerAddress string `envconfig:"server_address" required:"true"`
// PrivateKey string `envconfig:"private_key" required:"true"`
// TenantID string `envconfig:"tenant_id" required:"true"`
// }
//
// This behavior is driven by the [envconfig] package. Check it out for more information.
//
// [envconfig]: https://github.com/kelseyhightower/envconfig
cfg, err := envs.ParseWithPrefix[agent.Config]("shellhub")
if err != nil {
envconfig.Usage("shellhub", &cfg) // nolint:errcheck
log.Fatal(err)
}
Expand Down Expand Up @@ -88,7 +104,7 @@ func main() {
"mode": mode,
}).Info("Starting ShellHub")

ag, err := agent.NewAgentWithConfig(&cfg)
ag, err := agent.NewAgentWithConfig(cfg)
if err != nil {
log.WithError(err).WithFields(log.Fields{
"version": AgentVersion,
Expand Down
10 changes: 5 additions & 5 deletions api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package main
import (
"context"

"github.com/kelseyhightower/envconfig"
"github.com/shellhub-io/shellhub/pkg/envs"
"github.com/shellhub-io/shellhub/pkg/loglevel"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand All @@ -18,13 +18,13 @@ func main() {

rootCmd.AddCommand(serverCmd)

// Populates configuration based on environment variables prefixed with 'API_'
var cfg config
if err := envconfig.Process("api", &cfg); err != nil {
// Populates configuration based on environment variables prefixed with 'API_'.
cfg, err := envs.ParseWithPrefix[config]("api")
if err != nil {
logrus.WithError(err).Fatal("Failed to load environment variables")
}

ctx := context.WithValue(context.TODO(), "cfg", &cfg) //nolint:revive
ctx := context.WithValue(context.TODO(), "cfg", cfg) //nolint:revive

if err := rootCmd.ExecuteContext(ctx); err != nil {
logrus.Fatal(err)
Expand Down
10 changes: 4 additions & 6 deletions api/workers/workers.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package workers

import (
"github.com/kelseyhightower/envconfig"
)
import "github.com/shellhub-io/shellhub/pkg/envs"

type Envs struct {
MongoURI string `envconfig:"mongo_uri" default:"mongodb://mongo:27017/main"`
Expand Down Expand Up @@ -30,10 +28,10 @@ type Envs struct {
}

func getEnvs() (*Envs, error) {
var envs Envs
if err := envconfig.Process("api", &envs); err != nil {
env, err := envs.ParseWithPrefix[Envs]("api")
if err != nil {
return nil, err
}

return &envs, nil
return env, nil
}
6 changes: 3 additions & 3 deletions cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package main
import (
"context"

"github.com/kelseyhightower/envconfig"
"github.com/shellhub-io/shellhub/api/store/mongo"
"github.com/shellhub-io/shellhub/cli/cmd"
"github.com/shellhub-io/shellhub/cli/services"
storecache "github.com/shellhub-io/shellhub/pkg/cache"
"github.com/shellhub-io/shellhub/pkg/envs"
"github.com/shellhub-io/shellhub/pkg/loglevel"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand All @@ -26,8 +26,8 @@ func init() {
}

func main() {
var cfg config
if err := envconfig.Process("cli", &cfg); err != nil {
cfg, err := envs.ParseWithPrefix[config]("cli")
if err != nil {
log.Error(err.Error())
}

Expand Down
5 changes: 2 additions & 3 deletions pkg/api/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
"path"

"github.com/go-resty/resty/v2"
"github.com/kelseyhightower/envconfig"
client "github.com/shellhub-io/shellhub/pkg/api/internalclient"
"github.com/shellhub-io/shellhub/pkg/envs"
"github.com/shellhub-io/shellhub/pkg/uuid"
"github.com/sirupsen/logrus"
)
Expand All @@ -36,8 +36,7 @@ type Options struct {
func NewClient() Webhook {
httpClient := resty.New()
httpClient.SetRetryCount(3)
opts := Options{}
err := envconfig.Process("", &opts)
opts, err := envs.ParseWithPrefix[Options]("")
if err != nil {
return nil
}
Expand Down
10 changes: 9 additions & 1 deletion pkg/envs/backend_env.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
package envs

import "os"
import (
"os"

"github.com/kelseyhightower/envconfig"
)

type envBackend struct{}

// envBackend is the default key/value store that reads from environment variables.
func (b *envBackend) Get(name string) string {
return os.Getenv(name)
}

func (b *envBackend) Process(prefix string, spec interface{}) error {
return envconfig.Process(prefix, spec)
}
26 changes: 26 additions & 0 deletions pkg/envs/envs.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package envs

import (
"errors"
)

const (
ENABLED = "true"
)

// Backend is an interface for any sort of underlying key/value store.
type Backend interface {
Get(key string) string
Process(prefix string, spec interface{}) error
}

// DefaultBackend define the backend to be used to get environment variables.
Expand Down Expand Up @@ -36,3 +41,24 @@ func HasBilling() bool {
func IsCommunity() bool {
return (DefaultBackend.Get("SHELLHUB_CLOUD") != ENABLED && DefaultBackend.Get("SHELLHUB_ENTERPRISE") != ENABLED)
}

var ErrParse = errors.New("failed to parse environment variables for the given prefix")

// ParseWithPrefix parses the environment variables for the a given prefix.
//
// This function uses the [envconfig] package as its default backend, so it requires the struct to be annotated with
// the [envconfig] tags. Check the [envconfig] documentation for more information.
//
// The T generic parameter must be a struct with the fields annotated with the [envconfig] tags, that will be returned
// with the values parsed from the environment variables.
//
// [envconfig]: https://github.com/kelseyhightower/envconfig
func ParseWithPrefix[T any](prefix string) (*T, error) {
envs := new(T)

if err := DefaultBackend.Process(prefix, envs); err != nil {
return nil, errors.Join(ErrParse, err)
}

return envs, nil
}
Loading

0 comments on commit 20395db

Please sign in to comment.