Skip to content

Commit

Permalink
Initial implementation of multi app run for Kubernetes Dev (#1333)
Browse files Browse the repository at this point in the history
* initial commit for multi-app run k8s impl

Signed-off-by: Mukundan Sundararajan <[email protected]>

* fix import

Signed-off-by: Mukundan Sundararajan <[email protected]>

* move runfileconfig

Signed-off-by: Mukundan Sundararajan <[email protected]>

* add protobuf conflict warn env

Signed-off-by: Mukundan Sundararajan <[email protected]>

* Add pubsub component. Check before component creation.

Signed-off-by: Mukundan Sundararajan <[email protected]>

* fix e2e

Signed-off-by: Mukundan Sundararajan <[email protected]>

* fix e2e

Signed-off-by: Mukundan Sundararajan <[email protected]>

* fix e2e

Signed-off-by: Mukundan Sundararajan <[email protected]>

* address review comments.

Signed-off-by: Mukundan Sundararajan <[email protected]>

---------

Signed-off-by: Mukundan Sundararajan <[email protected]>
  • Loading branch information
mukundansundar authored Sep 4, 2023
1 parent 6738eef commit a15a3eb
Show file tree
Hide file tree
Showing 42 changed files with 1,404 additions and 169 deletions.
1 change: 1 addition & 0 deletions .github/workflows/self_hosted_e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ jobs:
DAPR_DASHBOARD_PINNED_VERSION: 0.13.0
DAPR_RUNTIME_LATEST_STABLE_VERSION:
DAPR_DASHBOARD_LATEST_STABLE_VERSION:
GOLANG_PROTOBUF_REGISTRATION_CONFLICT: warn
PODMAN_VERSION: 4.4.4
strategy:
# TODO: Remove this when our E2E tests are stable for podman on MacOS.
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ cli

# CLI's auto-generated components directory
**/components
# Auto generated deploy dir inside .dapr directory
**/.dapr/deploy
# Auto generated logs dir inside .dapr directory
**/.dapr/logs

Expand Down
6 changes: 6 additions & 0 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var (
wait bool
timeout uint
slimMode bool
devMode bool
runtimeVersion string
dashboardVersion string
allNamespaces bool
Expand Down Expand Up @@ -68,6 +69,9 @@ dapr init --image-registry <registry-url>
# Initialize Dapr in Kubernetes
dapr init -k
# Initialize Dapr in Kubernetes in dev mode
dapr init -k --dev
# Initialize Dapr in Kubernetes and wait for the installation to complete (default timeout is 300s/5m)
dapr init -k --wait --timeout 600
Expand Down Expand Up @@ -127,6 +131,7 @@ dapr init --runtime-path <path-to-install-directory>
DashboardVersion: dashboardVersion,
EnableMTLS: enableMTLS,
EnableHA: enableHA,
EnableDev: devMode,
Args: values,
Wait: wait,
Timeout: timeout,
Expand Down Expand Up @@ -202,6 +207,7 @@ func init() {
defaultContainerRuntime := string(utils.DOCKER)

InitCmd.Flags().BoolVarP(&kubernetesMode, "kubernetes", "k", false, "Deploy Dapr to a Kubernetes cluster")
InitCmd.Flags().BoolVarP(&devMode, "dev", "", false, "Use Dev mode. Deploy Redis, Zipkin also in the Kubernetes cluster")
InitCmd.Flags().BoolVarP(&wait, "wait", "", false, "Wait for Kubernetes initialization to complete")
InitCmd.Flags().UintVarP(&timeout, "timeout", "", 300, "The wait timeout for the Kubernetes installation")
InitCmd.Flags().BoolVarP(&slimMode, "slim", "s", false, "Exclude placement service, Redis and Zipkin containers from self-hosted installation")
Expand Down
77 changes: 30 additions & 47 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ import (
"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/dapr/cli/pkg/kubernetes"
"github.com/dapr/cli/pkg/metadata"
"github.com/dapr/cli/pkg/print"
runExec "github.com/dapr/cli/pkg/runexec"
"github.com/dapr/cli/pkg/runfileconfig"
"github.com/dapr/cli/pkg/standalone"
"github.com/dapr/cli/pkg/standalone/runfileconfig"
daprsyscall "github.com/dapr/cli/pkg/syscall"
"github.com/dapr/cli/utils"
)
Expand Down Expand Up @@ -64,6 +65,7 @@ var (
apiListenAddresses string
runFilePath string
appChannelAddress string
enableRunK8s bool
)

const (
Expand Down Expand Up @@ -105,6 +107,12 @@ dapr run --run-file dapr.yaml
# Run multiple apps by providing a directory path containing the run config file(dapr.yaml)
dapr run --run-file /path/to/directory
# Run multiple apps in Kubernetes by proficing path of a run config file
dapr run --run-file dapr.yaml -k
# Run multiple apps in Kubernetes by providing a directory path containing the run config file(dapr.yaml)
dapr run --run-file /path/to/directory -k
`,
Args: cobra.MinimumNArgs(0),
PreRun: func(cmd *cobra.Command, args []string) {
Expand All @@ -117,7 +125,7 @@ dapr run --run-file /path/to/directory
print.FailureStatusEvent(os.Stderr, "Failed to get run file path: %v", err)
os.Exit(1)
}
executeRunWithAppsConfigFile(runConfigFilePath)
executeRunWithAppsConfigFile(runConfigFilePath, enableRunK8s)
return
}
if len(args) == 0 {
Expand Down Expand Up @@ -457,6 +465,7 @@ func init() {
RunCmd.Flags().IntVar(&appHealthTimeout, "app-health-probe-timeout", 0, "Timeout for app health probes in milliseconds")
RunCmd.Flags().IntVar(&appHealthThreshold, "app-health-threshold", 0, "Number of consecutive failures for the app to be considered unhealthy")
RunCmd.Flags().BoolVar(&enableAPILogging, "enable-api-logging", false, "Log API calls at INFO verbosity. Valid values are: true or false")
RunCmd.Flags().BoolVarP(&enableRunK8s, "kubernetes", "k", false, "Run the multi-app run template against Kubernetes environment.")
RunCmd.Flags().StringVar(&apiListenAddresses, "dapr-listen-addresses", "", "Comma separated list of IP addresses that sidecar will listen to")
RunCmd.Flags().StringVarP(&runFilePath, "run-file", "f", "", "Path to the run template file for the list of apps to run")
RunCmd.Flags().StringVarP(&appChannelAddress, "app-channel-address", "", utils.DefaultAppChannelAddress, "The network address the application listens on")
Expand Down Expand Up @@ -507,11 +516,11 @@ func executeRun(runTemplateName, runFilePath string, apps []runfileconfig.App) (
// A custom writer used for trimming ASCII color codes from logs when writing to files.
var customAppLogWriter io.Writer

daprdLogWriterCloser := getLogWriter(app.DaprdLogWriteCloser, app.DaprdLogDestination)
daprdLogWriterCloser := runfileconfig.GetLogWriter(app.DaprdLogWriteCloser, app.DaprdLogDestination)

if len(runConfig.Command) == 0 {
print.StatusEvent(os.Stdout, print.LogWarning, "No application command found for app %q present in %s", runConfig.AppID, runFilePath)
appDaprdWriter = getAppDaprdWriter(app, true)
appDaprdWriter = runExec.GetAppDaprdWriter(app, true)
appLogWriter = app.DaprdLogWriteCloser
} else {
err = app.CreateAppLogFile()
Expand All @@ -520,8 +529,8 @@ func executeRun(runTemplateName, runFilePath string, apps []runfileconfig.App) (
exitWithError = true
break
}
appDaprdWriter = getAppDaprdWriter(app, false)
appLogWriter = getLogWriter(app.AppLogWriteCloser, app.AppLogDestination)
appDaprdWriter = runExec.GetAppDaprdWriter(app, false)
appLogWriter = runfileconfig.GetLogWriter(app.AppLogWriteCloser, app.AppLogDestination)
}
customAppLogWriter = print.CustomLogWriter{W: appLogWriter}
runState, err := startDaprdAndAppProcesses(&runConfig, app.AppDirPath, sigCh,
Expand Down Expand Up @@ -590,43 +599,6 @@ func executeRun(runTemplateName, runFilePath string, apps []runfileconfig.App) (
return exitWithError, closeError
}

// getAppDaprdWriter returns the writer for writing logs common to both daprd, app and stdout.
func getAppDaprdWriter(app runfileconfig.App, isAppCommandEmpty bool) io.Writer {
var appDaprdWriter io.Writer
if isAppCommandEmpty {
if app.DaprdLogDestination != standalone.Console {
appDaprdWriter = io.MultiWriter(os.Stdout, app.DaprdLogWriteCloser)
} else {
appDaprdWriter = os.Stdout
}
} else {
if app.AppLogDestination != standalone.Console && app.DaprdLogDestination != standalone.Console {
appDaprdWriter = io.MultiWriter(app.AppLogWriteCloser, app.DaprdLogWriteCloser, os.Stdout)
} else if app.AppLogDestination != standalone.Console {
appDaprdWriter = io.MultiWriter(app.AppLogWriteCloser, os.Stdout)
} else if app.DaprdLogDestination != standalone.Console {
appDaprdWriter = io.MultiWriter(app.DaprdLogWriteCloser, os.Stdout)
} else {
appDaprdWriter = os.Stdout
}
}
return appDaprdWriter
}

// getLogWriter returns the log writer based on the log destination.
func getLogWriter(fileLogWriterCloser io.WriteCloser, logDestination standalone.LogDestType) io.Writer {
var logWriter io.Writer
switch logDestination {
case standalone.Console:
logWriter = os.Stdout
case standalone.File:
logWriter = fileLogWriterCloser
case standalone.FileAndConsole:
logWriter = io.MultiWriter(os.Stdout, fileLogWriterCloser)
}
return logWriter
}

func logInformationalStatusToStdout(app runfileconfig.App) {
print.InfoStatusEvent(os.Stdout, "Started Dapr with app id %q. HTTP Port: %d. gRPC Port: %d",
app.AppID, app.RunConfig.HTTPPort, app.RunConfig.GRPCPort)
Expand All @@ -652,9 +624,8 @@ func gracefullyShutdownAppsAndCloseResources(runState []*runExec.RunExec, apps [
return err
}

func executeRunWithAppsConfigFile(runFilePath string) {
config := runfileconfig.RunFileConfig{}
apps, err := config.GetApps(runFilePath)
func executeRunWithAppsConfigFile(runFilePath string, k8sEnabled bool) {
config, apps, err := getRunConfigFromRunFile(runFilePath)
if err != nil {
print.StatusEvent(os.Stdout, print.LogFailure, "Error getting apps from config file: %s", err)
os.Exit(1)
Expand All @@ -663,7 +634,13 @@ func executeRunWithAppsConfigFile(runFilePath string) {
print.StatusEvent(os.Stdout, print.LogFailure, "No apps to run")
os.Exit(1)
}
exitWithError, closeErr := executeRun(config.Name, runFilePath, apps)
var exitWithError bool
var closeErr error
if !k8sEnabled {
exitWithError, closeErr = executeRun(config.Name, runFilePath, apps)
} else {
exitWithError, closeErr = kubernetes.Run(runFilePath, config)
}
if exitWithError {
if closeErr != nil {
print.StatusEvent(os.Stdout, print.LogFailure, "Error closing resources: %s", closeErr)
Expand All @@ -672,6 +649,12 @@ func executeRunWithAppsConfigFile(runFilePath string) {
}
}

func getRunConfigFromRunFile(runFilePath string) (runfileconfig.RunFileConfig, []runfileconfig.App, error) {
config := runfileconfig.RunFileConfig{}
apps, err := config.GetApps(runFilePath)
return config, apps, err
}

// startDaprdAndAppProcesses is a function to start the App process and the associated Daprd process.
// This should be called as a blocking function call.
func startDaprdAndAppProcesses(runConfig *standalone.RunConfig, commandDir string, sigCh chan os.Signal,
Expand Down
33 changes: 27 additions & 6 deletions cmd/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@ import (

"github.com/spf13/cobra"

"github.com/dapr/cli/pkg/kubernetes"
"github.com/dapr/cli/pkg/print"
"github.com/dapr/cli/pkg/standalone"
)

var stopAppID string
var (
stopAppID string
stopK8s bool
)

var StopCmd = &cobra.Command{
Use: "stop",
Expand All @@ -38,6 +42,12 @@ dapr stop --run-file dapr.yaml
# Stop multiple apps by providing a directory path containing the run config file(dapr.yaml)
dapr stop --run-file /path/to/directory
# Stop and delete Kubernetes deployment of multiple apps by providing a run config file
dapr stop --run-file dapr.yaml -k
# Stop and delete Kubernetes deployment of multiple apps by providing a directory path containing the run config file(dapr.yaml)
dapr stop --run-file /path/to/directory -k
`,
Run: func(cmd *cobra.Command, args []string) {
var err error
Expand All @@ -47,13 +57,23 @@ dapr stop --run-file /path/to/directory
print.FailureStatusEvent(os.Stderr, "Failed to get run file path: %v", err)
os.Exit(1)
}
err = executeStopWithRunFile(runFilePath)
if !stopK8s {
err = executeStopWithRunFile(runFilePath)
if err != nil {
print.FailureStatusEvent(os.Stderr, "Failed to stop Dapr and app processes: %s", err)
} else {
print.SuccessStatusEvent(os.Stdout, "Dapr and app processes stopped successfully")
}
return
}
config, _, cErr := getRunConfigFromRunFile(runFilePath)
if cErr != nil {
print.FailureStatusEvent(os.Stderr, "Failed to parse run template file %q: %s", runFilePath, cErr.Error())
}
err = kubernetes.Stop(runFilePath, config)
if err != nil {
print.FailureStatusEvent(os.Stderr, "Failed to stop Dapr and app processes: %s", err)
} else {
print.SuccessStatusEvent(os.Stdout, "Dapr and app processes stopped successfully")
print.FailureStatusEvent(os.Stderr, "Error stopping deployments from multi-app run template: %v", err)
}
return
}
if stopAppID != "" {
args = append(args, stopAppID)
Expand All @@ -78,6 +98,7 @@ dapr stop --run-file /path/to/directory
func init() {
StopCmd.Flags().StringVarP(&stopAppID, "app-id", "a", "", "The application id to be stopped")
StopCmd.Flags().StringVarP(&runFilePath, "run-file", "f", "", "Path to the run template file for the list of apps to stop")
StopCmd.Flags().BoolVarP(&stopK8s, "kubernetes", "k", false, "Stop deployments in Kunernetes based on multi-app run file")
StopCmd.Flags().BoolP("help", "h", false, "Print this help message")
RootCmd.AddCommand(StopCmd)
}
Expand Down
13 changes: 12 additions & 1 deletion cmd/uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
var (
uninstallNamespace string
uninstallKubernetes bool
uninstallDev bool
uninstallAll bool
uninstallContainerRuntime string
)
Expand All @@ -48,6 +49,15 @@ dapr uninstall --all
# Uninstall from Kubernetes
dapr uninstall -k
# Uninstall from Kubernetes and remove CRDs
dapr uninstall -k --all
# Uninstall from Kubernetes remove dev deployments of Redis, Zipkin
dapr uninstall -k --dev
# Uninstall from Kubernetes remove dev deployments of Redis, Zipkin and CRDs
dapr uninstall -k --dev --all
# Uninstall Dapr from non-default install directory
# This will remove the .dapr directory present in the path <path-to-install-directory>
dapr uninstall --runtime-path <path-to-install-directory>
Expand All @@ -66,7 +76,7 @@ dapr uninstall --runtime-path <path-to-install-directory>
}

print.InfoStatusEvent(os.Stdout, "Removing Dapr from your cluster...")
err = kubernetes.Uninstall(uninstallNamespace, uninstallAll, timeout)
err = kubernetes.Uninstall(uninstallNamespace, uninstallAll, uninstallDev, timeout)
} else {
if !utils.IsValidContainerRuntime(uninstallContainerRuntime) {
print.FailureStatusEvent(os.Stdout, "Invalid container runtime. Supported values are docker and podman.")
Expand All @@ -87,6 +97,7 @@ dapr uninstall --runtime-path <path-to-install-directory>

func init() {
UninstallCmd.Flags().BoolVarP(&uninstallKubernetes, "kubernetes", "k", false, "Uninstall Dapr from a Kubernetes cluster")
UninstallCmd.Flags().BoolVarP(&uninstallDev, "dev", "", false, "Uninstall Dapr Redis and Zipking installations from Kubernetes cluster")
UninstallCmd.Flags().UintVarP(&timeout, "timeout", "", 300, "The timeout for the Kubernetes uninstall")
UninstallCmd.Flags().BoolVar(&uninstallAll, "all", false, "Remove .dapr directory, Redis, Placement and Zipkin containers on local machine, and CRDs on a Kubernetes cluster")
UninstallCmd.Flags().String("network", "", "The Docker network from which to remove the Dapr runtime")
Expand Down
39 changes: 28 additions & 11 deletions pkg/kubernetes/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/dapr/cli/pkg/age"
"github.com/dapr/cli/utils"
v1alpha1 "github.com/dapr/dapr/pkg/apis/components/v1alpha1"
"github.com/dapr/dapr/pkg/client/clientset/versioned"
)

// ComponentsOutput represent a Dapr component.
Expand All @@ -46,19 +47,35 @@ func PrintComponents(name, namespace, outputFormat string) error {
return nil, err
}

list, err := client.ComponentsV1alpha1().Components(namespace).List(meta_v1.ListOptions{})
// This means that the Dapr Components CRD is not installed and
// therefore no component items exist.
if apierrors.IsNotFound(err) {
list = &v1alpha1.ComponentList{
Items: []v1alpha1.Component{},
}
} else if err != nil {
return nil, err
return listComponents(client, namespace)
}, name, outputFormat)
}

func listComponents(client versioned.Interface, namespace string) (*v1alpha1.ComponentList, error) {
list, err := client.ComponentsV1alpha1().Components(namespace).List(meta_v1.ListOptions{})
// This means that the Dapr Components CRD is not installed and
// therefore no component items exist.
if apierrors.IsNotFound(err) {
list = &v1alpha1.ComponentList{
Items: []v1alpha1.Component{},
}
} else if err != nil {
return nil, err
}

return list, nil
}, name, outputFormat)
return list, nil
}

func getComponent(client versioned.Interface, namespace string, componentName string) (*v1alpha1.Component, error) {
c, err := client.ComponentsV1alpha1().Components(namespace).Get(componentName, meta_v1.GetOptions{})
// This means that the Dapr Components CRD is not installed and
// therefore no component items exist.
if apierrors.IsNotFound(err) {
return &v1alpha1.Component{}, nil
} else if err != nil {
return nil, err
}
return c, err
}

func writeComponents(writer io.Writer, getConfigFunc func() (*v1alpha1.ComponentList, error), name, outputFormat string) error {
Expand Down
Loading

0 comments on commit a15a3eb

Please sign in to comment.