mirror of https://github.com/dapr/cli.git
241 lines
8.1 KiB
Go
241 lines
8.1 KiB
Go
/*
|
|
Copyright 2022 The Dapr Authors
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package runfileconfig
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/dapr/cli/pkg/standalone"
|
|
"github.com/dapr/cli/utils"
|
|
|
|
"gopkg.in/yaml.v2"
|
|
)
|
|
|
|
// Parse the provided run file into a RunFileConfig struct.
|
|
func (a *RunFileConfig) parseAppsConfig(runFilePath string) error {
|
|
var err error
|
|
bytes, err := utils.ReadFile(runFilePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = yaml.Unmarshal(bytes, &a)
|
|
if err != nil {
|
|
return fmt.Errorf("error in parsing the provided app config file: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// validateRunConfig validates the run file config for mandatory fields.
|
|
// It also resolves relative paths to absolute paths and validates them.
|
|
func (a *RunFileConfig) validateRunConfig(runFilePath string) error {
|
|
baseDir, err := filepath.Abs(filepath.Dir(runFilePath))
|
|
if err != nil {
|
|
return fmt.Errorf("error in getting the absolute path of the provided run template file: %w", err)
|
|
}
|
|
if a.Version == 0 {
|
|
return errors.New("required field 'version' not found in the provided run template file")
|
|
}
|
|
|
|
// resolve relative path to absolute and validate all paths in commons.
|
|
err = a.resolvePathToAbsAndValidate(baseDir, &a.Common.ConfigFile, &a.Common.ResourcesPath, &a.Common.DaprdInstallPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i := 0; i < len(a.Apps); i++ {
|
|
if a.Apps[i].AppDirPath == "" {
|
|
return errors.New("required field 'appDirPath' not found in the provided app config file")
|
|
}
|
|
// It resolves the relative AppDirPath to absolute path and validates it.
|
|
err := a.resolvePathToAbsAndValidate(baseDir, &a.Apps[i].AppDirPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// All other relative paths present inside the specific app's in the YAML file, should be resolved relative to AppDirPath for that app.
|
|
err = a.resolvePathToAbsAndValidate(a.Apps[i].AppDirPath, &a.Apps[i].ConfigFile, &a.Apps[i].ResourcesPath, &a.Apps[i].DaprdInstallPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetApps orchestrates the parsing of supplied run file, validating fields and consolidating SharedRunConfig for the apps.
|
|
// It returns a list of apps with the merged values for the SharedRunConfig from common section of the YAML file.
|
|
func (a *RunFileConfig) GetApps(runFilePath string) ([]App, error) {
|
|
err := a.parseAppsConfig(runFilePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = a.validateRunConfig(runFilePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = a.resolveResourcesAndConfigFilePaths()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
a.mergeCommonAndAppsSharedRunConfig()
|
|
a.mergeCommonAndAppsEnv()
|
|
// Resolve app ids if not provided in the run file.
|
|
err = a.setAppIDIfEmpty()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return a.Apps, nil
|
|
}
|
|
|
|
// mergeCommonAndAppsSharedRunConfig merges the common section of the run file with the apps section.
|
|
func (a *RunFileConfig) mergeCommonAndAppsSharedRunConfig() {
|
|
sharedConfigType := reflect.TypeOf(a.Common.SharedRunConfig)
|
|
fields := reflect.VisibleFields(sharedConfigType)
|
|
// Iterate for each field in common(shared configurations).
|
|
for _, field := range fields {
|
|
val := reflect.ValueOf(a.Common.SharedRunConfig).FieldByName(field.Name)
|
|
// Iterate for each app's configurations.
|
|
for i := range a.Apps {
|
|
appVal := reflect.ValueOf(a.Apps[i].RunConfig.SharedRunConfig).FieldByName(field.Name)
|
|
// If apppVal is the default value for the type.
|
|
if appVal.IsZero() {
|
|
// Here FieldByName always returns a valid value, it can also be zero but the field always exists.
|
|
reflect.ValueOf(&a.Apps[i].RunConfig.SharedRunConfig).
|
|
Elem().
|
|
FieldByName(field.Name).
|
|
Set(val)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set AppID to the directory name of appDirPath.
|
|
// appDirPath is a mandatory field in the run file and at this point it is already validated and resolved to its absolute path.
|
|
func (a *RunFileConfig) setAppIDIfEmpty() error {
|
|
for i := range a.Apps {
|
|
if a.Apps[i].AppID == "" {
|
|
basePath, err := a.getBasePathFromAbsPath(a.Apps[i].AppDirPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.Apps[i].AppID = basePath
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Gets the base path from the absolute path of the appDirPath.
|
|
func (a *RunFileConfig) getBasePathFromAbsPath(appDirPath string) (string, error) {
|
|
if filepath.IsAbs(appDirPath) {
|
|
return filepath.Base(appDirPath), nil
|
|
}
|
|
return "", fmt.Errorf("error in getting the base path from the provided appDirPath %q: ", appDirPath)
|
|
}
|
|
|
|
// resolvePathToAbsAndValidate resolves the relative paths in run file to absolute path and validates the file path.
|
|
func (a *RunFileConfig) resolvePathToAbsAndValidate(baseDir string, paths ...*string) error {
|
|
var err error
|
|
for _, path := range paths {
|
|
if *path == "" {
|
|
continue
|
|
}
|
|
absPath := utils.GetAbsPath(baseDir, *path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*path = absPath
|
|
if err = utils.ValidateFilePath(*path); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Resolve resources and config file paths for each app.
|
|
func (a *RunFileConfig) resolveResourcesAndConfigFilePaths() error {
|
|
for i := range a.Apps {
|
|
app := &a.Apps[i]
|
|
// Make sure apps's "DaprPathCmdFlag" is updated here as it is used in deciding precedence for resources and config path.
|
|
if app.DaprdInstallPath == "" {
|
|
app.DaprdInstallPath = a.Common.DaprdInstallPath
|
|
}
|
|
|
|
err := a.resolveResourcesFilePath(app)
|
|
if err != nil {
|
|
return fmt.Errorf("error in resolving resources path for app %q: %w", app.AppID, err)
|
|
}
|
|
|
|
err = a.resolveConfigFilePath(app)
|
|
if err != nil {
|
|
return fmt.Errorf("error in resolving config file path for app %q: %w", app.AppID, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// mergeCommonAndAppsEnv merges env maps from common and individual apps.
|
|
// Precedence order for envs -> apps[i].envs > common.envs.
|
|
func (a *RunFileConfig) mergeCommonAndAppsEnv() {
|
|
for i := range a.Apps {
|
|
for k, v := range a.Common.Env {
|
|
if _, ok := a.Apps[i].Env[k]; !ok {
|
|
a.Apps[i].Env[k] = v
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// resolveResourcesFilePath resolves the resources path for the app.
|
|
// Precedence order for resourcesPath -> apps[i].resourcesPath > apps[i].appDirPath/.dapr/resources > common.resourcesPath > dapr default resources path.
|
|
func (a *RunFileConfig) resolveResourcesFilePath(app *App) error {
|
|
if app.ResourcesPath != "" {
|
|
return nil
|
|
}
|
|
localResourcesDir := filepath.Join(app.AppDirPath, standalone.DefaultDaprDirName, standalone.DefaultResourcesDirName)
|
|
if err := utils.ValidateFilePath(localResourcesDir); err == nil {
|
|
app.ResourcesPath = localResourcesDir
|
|
} else if len(strings.TrimSpace(a.Common.ResourcesPath)) > 0 {
|
|
app.ResourcesPath = a.Common.ResourcesPath
|
|
} else {
|
|
daprDirPath, err := standalone.GetDaprPath(app.DaprdInstallPath)
|
|
if err != nil {
|
|
return fmt.Errorf("error getting dapr install path: %w", err)
|
|
}
|
|
app.ResourcesPath = standalone.GetDaprComponentsPath(daprDirPath)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// resolveConfigFilePath resolves the config file path for the app.
|
|
// Precedence order for configFile -> apps[i].configFile > apps[i].appDirPath/.dapr/config.yaml > common.configFile > dapr default config file.
|
|
func (a *RunFileConfig) resolveConfigFilePath(app *App) error {
|
|
if app.ConfigFile != "" {
|
|
return nil
|
|
}
|
|
localConfigFile := filepath.Join(app.AppDirPath, standalone.DefaultDaprDirName, standalone.DefaultConfigFileName)
|
|
if err := utils.ValidateFilePath(localConfigFile); err == nil {
|
|
app.ConfigFile = localConfigFile
|
|
} else if len(strings.TrimSpace(a.Common.ConfigFile)) > 0 {
|
|
app.ConfigFile = a.Common.ConfigFile
|
|
} else {
|
|
daprDirPath, err := standalone.GetDaprPath(app.DaprdInstallPath)
|
|
if err != nil {
|
|
return fmt.Errorf("error getting dapr install path: %w", err)
|
|
}
|
|
app.ConfigFile = standalone.GetDaprConfigPath(daprDirPath)
|
|
}
|
|
return nil
|
|
}
|