Skip to content

Dev: Code architecture

Philippe Martin edited this page Sep 13, 2023 · 14 revisions

Context

A go context is created at the very beginning of the execution of the go program, and is transmitted to the whole program.

import (
	"context"

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

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)		❷

Telemetry data

Thanks to ctx = segment.NewContext(ctx) (❶), the context is ready to accept data to be sent to the telemetry platform.

CLI and business layers can use functions provided by the pkg/segment/context package, to set data to be sent: SetComponentType, SetClusterType, etc.

For example, the CLI layer for the odo dev command uses this code to set the component type to be sent to the telemetry:

scontext.SetComponentType(ctx, component.GetComponentTypeFromDevfileMetadata(devFileObj.Data.GetMetadata()))

At the end of the execution of a command, the telemetry data is collected and sent, using the odo telemetry command as sub-process (see the use of the function startTelemetry in genericclioptions.GenericRun).

Configuration based on environment variables

odo accepts some environment variables to configure its behaviour.

Thanks to ctx = envcontext.WithEnvConfig(ctx, *envConfig) (❷), values of these environment variables are accessible from the context using the function GetEnvConfig from the pkg/config/context package. The value returned by this function is a structure containing pointers to values. A pointer points to nil when the associated environment variable is not defined and points to the value of the variable, when defined.

For example, to get the value of the PODMAN_CMD environment variable, or "podman" if the environment variable is not defined:

podmanCmd := pointer.StringDeref(envcontext.GetEnvConfig(ctx).PodmanCmd, "podman")

These values being injected in the context before calling the NewCmdOdo(ctx, ...) function, they are available when defining the odo commands and flags.

These values are also available when a specific command is executed, from the ctx parameter passed to the Complete, Validate and Run methods of the command.

Context of odo execution

Values defining the context on which odo is executed are stored in the context, and are accessible with specific functions from the pkg/odo/context package:

  • Application name (always "app" for now), with GetApplication(ctx),
  • Directory on which odo is executed, with GetWorkingDirectory(ctx),
  • Path of the devfile found in the working directory, with GetDevfilePath(ctx),
  • The content of the Devfile found in the working directory, with GetDevfileObj(ctx),
  • The name of the Devfile component (based on Devfile metadata name or, if not defined, the current directory name), with GetComponentName(ctx),
  • The current namespace of the Kubernetes/Openshift cluster, with GetNamespace(ctx).

These values are set in genericclioptions.GenericRun, and so:

  • they are available when a specific command is executed, from the ctx parameter passed to the Complete, Validate and Run methods of the command.
  • they are not available when defining the odo commands and flags.

These values are stored into the context only if they make sense for the executed command, based on the dependencies declared for the command using clientset.Add:

  • Namespace is stored only in the context if the KUBERNETES dependency is requested.
  • Working directory, Devfile path, defile content and component name are stored in the context if the FILESYSTEM dependency is requested.

Common flags

Some flags are common to several commands. The values of these flags are stored in the context, and are accessible with specific functions from the pkg/odo/commonflags/context package.

  • If JSON output is requested with -o json flag, accessible with IsJsonOutput(ctx),
  • Which platform is targeted with --run-on *platform*, accessible with GetRunOn(ctx),
  • Which variables are passed with --var and/or --var-file flags, accessible with GetVariables(ctx).

These values are set in genericclioptions.GenericRun, and so:

  • they are available when a specific command is executed, from the ctx parameter passed to the Complete, Validate and Run methods of the command.
  • they are not available when defining the odo commands and flags.

These values are stored into the context only if they make sense for the executed command, based on the dependencies declared for the command using clientset.Add:

  • Variables are stored only if FILESYSTEM dependency is requested.

Dependency Injection

A dependency injection system gives access to modules by the commands. A command needs to register the modules it needs to access with the function clientset.Add.

The dependency injection system is implemented in pkg/odo/genericclioptions/clientset. Modules are registered there, as well as dependencies between modules (a module can declare it needs access to other modules).

The dependency injection system creates at startup one (and only one) instance of each module needed by the invoked command, directly or indirectly, and injects these dependencies into the command (using the SetClientset method) and the modules (using the helper function NewClient(deps …) provided by each module).