Skip to content

Commit

Permalink
Centralize environment configuration (#6293)
Browse files Browse the repository at this point in the history
* Define central config

* Use envConfig in GenericRun for segment parameters

* Pass the env config into the context passed to CLI methods

* Use PodmanCmd and DockerCmd from context

* Remove tests now that ODO_DISABLE_TELEMETRY is checked for a bool value

* deploy.Deploy: Use values from ctx instead of parameters + use FS from DI

* dev.Start: Use values from ctx instead of parameters

* image.Build*: Use values from ctx instead of parameters

* Use telemetry file from context

* Pass ctx to segment.getTelemetryForDevfileRegistry

* Use ctx in getTelemetryForDevfileRegistry

* Call IsTelemetryEnabled once and use scontext.GetTelemetryStatus after

* Fix unit tests

* Use envConfig in segment.IsTelemetryEnabled

* Define TelemetryCaller constant in test helper

* IsTrackingConsentEnabled: get value from envconfig instead of env

* Use ctx instead of GLOBALODOCONFIG

* Place ODO_EXPERIMENTAL_MODE in configuration

* Use maintained envconfig package

* Define default values when exist

* Document accepted boolean values
  • Loading branch information
feloy authored Nov 17, 2022
1 parent ce42ce4 commit 712254a
Show file tree
Hide file tree
Showing 80 changed files with 2,089 additions and 449 deletions.
6 changes: 4 additions & 2 deletions cmd/cli-doc/cli-doc.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"context"
"errors"
"fmt"
"os"
Expand Down Expand Up @@ -171,11 +172,12 @@ func main() {
if len(args) == 0 {
fmt.Print(command.Usage())
} else {
ctx := context.Background()
switch args[0] {
case "reference":
fmt.Print(referencePrinter(cli.NewCmdOdo(cli.OdoRecommendedName, cli.OdoRecommendedName), 0))
fmt.Print(referencePrinter(cli.NewCmdOdo(ctx, cli.OdoRecommendedName, cli.OdoRecommendedName), 0))
case "structure":
fmt.Print(commandPrinter(cli.NewCmdOdo(cli.OdoRecommendedName, cli.OdoRecommendedName), 0))
fmt.Print(commandPrinter(cli.NewCmdOdo(ctx, cli.OdoRecommendedName, cli.OdoRecommendedName), 0))
default:
fmt.Print(command.Usage())
}
Expand Down
27 changes: 20 additions & 7 deletions cmd/odo/odo.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,36 @@ import (
"os"

"github.com/posener/complete"
"github.com/spf13/cobra"
"github.com/spf13/pflag"

"github.com/redhat-developer/odo/pkg/config"
envcontext "github.com/redhat-developer/odo/pkg/config/context"
"github.com/redhat-developer/odo/pkg/log"
"github.com/redhat-developer/odo/pkg/odo/cli"
"github.com/redhat-developer/odo/pkg/odo/cli/version"
"github.com/redhat-developer/odo/pkg/odo/util"
"github.com/redhat-developer/odo/pkg/odo/util/completion"
"github.com/redhat-developer/odo/pkg/preference"
segment "github.com/redhat-developer/odo/pkg/segment/context"
"github.com/spf13/cobra"
"github.com/spf13/pflag"

"k8s.io/klog"
)

func main() {
// Create a context ready for receiving telemetry data
// and save into it configuration based on environment variables
ctx := segment.NewContext(context.Background())
envConfig, err := config.GetConfiguration()
if err != nil {
util.LogErrorAndExit(err, "")
}
ctx = envcontext.WithEnvConfig(ctx, *envConfig)

// create the complete command
klog.InitFlags(nil)

root := cli.NewCmdOdo(cli.OdoRecommendedName, cli.OdoRecommendedName)
root := cli.NewCmdOdo(ctx, cli.OdoRecommendedName, cli.OdoRecommendedName)
rootCmp := createCompletion(root)
cmp := complete.New("odo", rootCmp)

Expand All @@ -41,7 +54,7 @@ func main() {
// parse the flags but hack around to avoid exiting with error code 2 on help
flag.CommandLine.Init(os.Args[0], flag.ContinueOnError)
args := os.Args[1:]
if err := flag.CommandLine.Parse(args); err != nil {
if err = flag.CommandLine.Parse(args); err != nil {
if err == flag.ErrHelp {
os.Exit(0)
}
Expand All @@ -55,7 +68,7 @@ func main() {
return
}

cfg, err := preference.NewClient()
cfg, err := preference.NewClient(ctx)
if err != nil {
util.LogErrorAndExit(err, "")
}
Expand All @@ -67,15 +80,15 @@ func main() {
updateInfo := make(chan string)
go version.GetLatestReleaseInfo(updateInfo)

util.LogErrorAndExit(root.ExecuteContext(segment.NewContext(context.Background())), "")
util.LogErrorAndExit(root.ExecuteContext(ctx), "")
select {
case message := <-updateInfo:
log.Info(message)
default:
klog.V(4).Info("Could not get the latest release information in time. Never mind, exiting gracefully :)")
}
} else {
util.LogErrorAndExit(root.ExecuteContext(segment.NewContext(context.Background())), "")
util.LogErrorAndExit(root.ExecuteContext(ctx), "")
}
}

Expand Down
4 changes: 3 additions & 1 deletion docs/website/docs/overview/configure.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,6 @@ Options here are mostly used for debugging and testing `odo` behavior.
| `DEVFILE_PROXY` | Integration tests will use this address as Devfile registry instead of `registry.stage.devfile.io` | v3.0.0-beta3 | `my-registry.example.com` |
| `TELEMETRY_CALLER` | Caller identifier passed to [telemetry](https://github.com/redhat-developer/odo/blob/main/USAGE_DATA.md). Case-insensitive. Acceptable values: `vscode`, `intellij`, `jboss`. | v3.1.0 | `intellij` |
| `ODO_TRACKING_CONSENT` | Useful for controlling [telemetry](https://github.com/redhat-developer/odo/blob/main/USAGE_DATA.md). Acceptable values: `yes` ([enables telemetry](https://github.com/redhat-developer/odo/blob/main/USAGE_DATA.md) and skips consent prompt), `no` (disables telemetry and consent prompt). Takes precedence over the [`ConsentTelemetry`](#preference-key-table) preference. | v3.2.0 | `yes` |
| `ODO_EXPERIMENTAL_MODE` | Whether to enable experimental features. See [Experimental Mode](../user-guides/advanced/experimental-mode) for more details. Acceptable values: `true` | v3.3.0 | `true` |
| `ODO_EXPERIMENTAL_MODE` | Whether to enable experimental features. See [Experimental Mode](../user-guides/advanced/experimental-mode) for more details. Acceptable values: boolean values<sup>(1)</sup> | v3.3.0 | `true` |

(1) Accepted boolean values are: `1`, `t`, `T`, `TRUE`, `true`, `True`, `0`, `f`, `F`, `FALSE`, `false`, `False`.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ require (
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
github.com/securego/gosec/v2 v2.14.0
github.com/segmentio/backo-go v1.0.1-0.20200129164019-23eae7c10bd3
github.com/sethvargo/go-envconfig v0.8.2
github.com/spf13/afero v1.6.0
github.com/spf13/cobra v1.4.0
github.com/spf13/pflag v1.0.5
Expand Down
5 changes: 3 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,7 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
Expand Down Expand Up @@ -883,8 +884,6 @@ github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
Expand Down Expand Up @@ -1052,6 +1051,8 @@ github.com/segmentio/backo-go v1.0.1-0.20200129164019-23eae7c10bd3/go.mod h1:9/R
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sethvargo/go-envconfig v0.8.2 h1:DDUVuG21RMgeB/bn4leclUI/837y6cQCD4w8hb5797k=
github.com/sethvargo/go-envconfig v0.8.2/go.mod h1:Iz1Gy1Sf3T64TQlJSvee81qDhf7YIlt8GMUX6yyNFs0=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
Expand Down
5 changes: 3 additions & 2 deletions pkg/alizer/alizer.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package alizer

import (
"context"
"fmt"
"os"
"path/filepath"
Expand All @@ -26,9 +27,9 @@ func NewAlizerClient(registryClient registry.Client) *Alizer {

// DetectFramework uses the alizer library in order to detect the devfile
// to use depending on the files in the path
func (o *Alizer) DetectFramework(path string) (recognizer.DevFileType, api.Registry, error) {
func (o *Alizer) DetectFramework(ctx context.Context, path string) (recognizer.DevFileType, api.Registry, error) {
types := []recognizer.DevFileType{}
components, err := o.registryClient.ListDevfileStacks("", "", "", false)
components, err := o.registryClient.ListDevfileStacks(ctx, "", "", "", false)
if err != nil {
return recognizer.DevFileType{}, api.Registry{}, err
}
Expand Down
6 changes: 4 additions & 2 deletions pkg/alizer/alizer_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package alizer

import (
"context"
"path/filepath"
"runtime"
"testing"
Expand Down Expand Up @@ -113,10 +114,11 @@ func TestDetectFramework(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
registryClient := registry.NewMockClient(ctrl)
registryClient.EXPECT().ListDevfileStacks("", "", "", false).Return(list, nil)
ctx := context.Background()
registryClient.EXPECT().ListDevfileStacks(ctx, "", "", "", false).Return(list, nil)
alizerClient := NewAlizerClient(registryClient)
// Run function DetectFramework
detected, registry, err := alizerClient.DetectFramework(tt.args.path)
detected, registry, err := alizerClient.DetectFramework(ctx, tt.args.path)

if !tt.wantErr == (err != nil) {
t.Errorf("unexpected error %v, wantErr %v", err, tt.wantErr)
Expand Down
4 changes: 3 additions & 1 deletion pkg/alizer/interface.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package alizer

import (
"context"

"github.com/redhat-developer/alizer/go/pkg/apis/recognizer"
"github.com/redhat-developer/odo/pkg/api"
)

type Client interface {
DetectFramework(path string) (recognizer.DevFileType, api.Registry, error)
DetectFramework(ctx context.Context, path string) (recognizer.DevFileType, api.Registry, error)
DetectName(path string) (string, error)
}
9 changes: 5 additions & 4 deletions pkg/alizer/mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package config

import (
"context"

"github.com/sethvargo/go-envconfig"
)

type Configuration struct {
DevfileProxy *string `env:"DEVFILE_PROXY,noinit"`
DockerCmd string `env:"DOCKER_CMD,default=docker"`
Globalodoconfig *string `env:"GLOBALODOCONFIG,noinit"`
OdoDebugTelemetryFile *string `env:"ODO_DEBUG_TELEMETRY_FILE,noinit"`
OdoDisableTelemetry *bool `env:"ODO_DISABLE_TELEMETRY,noinit"`
OdoLogLevel *int `env:"ODO_LOG_LEVEL,noinit"`
OdoTrackingConsent *string `env:"ODO_TRACKING_CONSENT,noinit"`
PodmanCmd string `env:"PODMAN_CMD,default=podman"`
TelemetryCaller string `env:"TELEMETRY_CALLER,default="`
OdoExperimentalMode bool `env:"ODO_EXPERIMENTAL_MODE,default=false"`
}

func GetConfiguration() (*Configuration, error) {
var s Configuration
err := envconfig.Process(context.Background(), &s)
if err != nil {
return nil, err
}
return &s, nil
}
54 changes: 54 additions & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package config

import (
"testing"
)

func TestDefaultValues(t *testing.T) {
cfg, err := GetConfiguration()
if err != nil {
t.Errorf("Error is not expected: %v", err)
}

checkDefaultStringValue(t, "DockerCmd", cfg.DockerCmd, "docker")
checkDefaultStringValue(t, "PodmanCmd", cfg.PodmanCmd, "podman")
checkDefaultStringValue(t, "TelemetryCaller", cfg.TelemetryCaller, "")
checkDefaultBoolValue(t, "OdoExperimentalMode", cfg.OdoExperimentalMode, false)

// Use noinit to set non initialized value as nil instead of zero-value
checkNilString(t, "DevfileProxy", cfg.DevfileProxy)
checkNilString(t, "Globalodoconfig", cfg.Globalodoconfig)
checkNilString(t, "Globalodoconfig", cfg.Globalodoconfig)
checkNilString(t, "OdoDebugTelemetryFile", cfg.OdoDebugTelemetryFile)
checkNilBool(t, "OdoDisableTelemetry", cfg.OdoDisableTelemetry)
checkNilString(t, "OdoTrackingConsent", cfg.OdoTrackingConsent)

}

func checkDefaultStringValue(t *testing.T, fieldName string, field string, def string) {
if field != def {
t.Errorf("default value for %q should be %q but is %q", fieldName, def, field)
}

}

func checkDefaultBoolValue(t *testing.T, fieldName string, field bool, def bool) {
if field != def {
t.Errorf("default value for %q should be %v but is %v", fieldName, def, field)
}

}

func checkNilString(t *testing.T, fieldName string, field *string) {
if field != nil {
t.Errorf("value for non specified env var %q should be nil but is %q", fieldName, *field)

}
}

func checkNilBool(t *testing.T, fieldName string, field *bool) {
if field != nil {
t.Errorf("value for non specified env var %q should be nil but is %v", fieldName, *field)

}
}
25 changes: 25 additions & 0 deletions pkg/config/context/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package context

import (
"context"

"github.com/redhat-developer/odo/pkg/config"
)

type contextKey struct{}

var key = contextKey{}

// WithEnvConfig sets the environment configuration in ctx
func WithEnvConfig(ctx context.Context, val config.Configuration) context.Context {
return context.WithValue(ctx, key, val)
}

// GetEnvConfig returns the environment configuration from ctx
func GetEnvConfig(ctx context.Context) config.Configuration {
value := ctx.Value(key)
if cast, ok := value.(config.Configuration); ok {
return cast
}
panic("GetEnvConfig can be called only after WithEnvConfig has been called")
}
Loading

0 comments on commit 712254a

Please sign in to comment.