From 420245bdd4c23f20115900d9d15c4dc136d6a4ee Mon Sep 17 00:00:00 2001 From: Randy Fay Date: Mon, 11 Jan 2021 08:57:05 -0700 Subject: [PATCH] Allow setting web environment in config.yaml, fixes #871 --- cmd/ddev/cmd/config-global.go | 14 ++++++++++++++ cmd/ddev/cmd/config.go | 12 ++++++++++++ docs/users/extend/config_yaml.md | 2 ++ .../extend/customization-extendibility.md | 18 ++++++------------ pkg/ddevapp/config.go | 12 +++++++++++- pkg/ddevapp/ddevapp.go | 1 + pkg/ddevapp/templates.go | 7 +++++++ pkg/globalconfig/global_config.go | 6 ++++++ 8 files changed, 59 insertions(+), 13 deletions(-) diff --git a/cmd/ddev/cmd/config-global.go b/cmd/ddev/cmd/config-global.go index 0af2bc9a794..3d366bb6d23 100644 --- a/cmd/ddev/cmd/config-global.go +++ b/cmd/ddev/cmd/config-global.go @@ -13,6 +13,8 @@ var ( instrumentationOptIn bool // omitContainers allows user to set value of omit_containers omitContainers string + // webEnvironmentGlobal allows user to set value of environment in web container + webEnvironmentGlobal string ) // configGlobalCommand is the the `ddev config global` command @@ -46,6 +48,16 @@ func handleGlobalConfig(cmd *cobra.Command, args []string) { } dirty = true } + if cmd.Flag("web-environment").Changed { + env := strings.Replace(webEnvironmentGlobal, " ", "", -1) + if env == "" { + globalconfig.DdevGlobalConfig.WebEnvironment = []string{} + } else { + globalconfig.DdevGlobalConfig.WebEnvironment = strings.Split(env, ",") + } + dirty = true + } + if cmd.Flag("nfs-mount-enabled").Changed { globalconfig.DdevGlobalConfig.NFSMountEnabledGlobal, _ = cmd.Flags().GetBool("nfs-mount-enabled") dirty = true @@ -105,6 +117,7 @@ func handleGlobalConfig(cmd *cobra.Command, args []string) { util.Success("Global configuration:") output.UserOut.Printf("instrumentation-opt-in=%v", globalconfig.DdevGlobalConfig.InstrumentationOptIn) output.UserOut.Printf("omit-containers=[%s]", strings.Join(globalconfig.DdevGlobalConfig.OmitContainersGlobal, ",")) + output.UserOut.Printf("web-environment=[%s]", strings.Join(globalconfig.DdevGlobalConfig.WebEnvironment, ",")) output.UserOut.Printf("nfs-mount-enabled=%v", globalconfig.DdevGlobalConfig.NFSMountEnabledGlobal) output.UserOut.Printf("router-bind-all-interfaces=%v", globalconfig.DdevGlobalConfig.RouterBindAllInterfaces) @@ -119,6 +132,7 @@ func handleGlobalConfig(cmd *cobra.Command, args []string) { func init() { configGlobalCommand.Flags().StringVarP(&omitContainers, "omit-containers", "", "", "For example, --omit-containers=dba,ddev-ssh-agent") + configGlobalCommand.Flags().StringVarP(&webEnvironmentGlobal, "web-environment", "", "", `Add environment variables to web container: --web-environment="TYPO3_CONTEXT=Development,SOMEENV=someval"`) configGlobalCommand.Flags().Bool("nfs-mount-enabled", false, "Enable NFS mounting on all projects globally") configGlobalCommand.Flags().BoolVarP(&instrumentationOptIn, "instrumentation-opt-in", "", false, "instrmentation-opt-in=true") configGlobalCommand.Flags().Bool("router-bind-all-interfaces", false, "router-bind-all-interfaces=true") diff --git a/cmd/ddev/cmd/config.go b/cmd/ddev/cmd/config.go index 4bf07ed2171..0fa1faa2104 100644 --- a/cmd/ddev/cmd/config.go +++ b/cmd/ddev/cmd/config.go @@ -139,6 +139,8 @@ var ( // ngrokArgs provides additional args to the ngrok command in `ddev share` ngrokArgs string + + webEnvironmentLocal string ) var providerName = nodeps.ProviderDefault @@ -248,6 +250,7 @@ func init() { ConfigCommand.Flags().StringVar(&additionalHostnamesArg, "additional-hostnames", "", "A comma-delimited list of hostnames for the project") ConfigCommand.Flags().StringVar(&additionalFQDNsArg, "additional-fqdns", "", "A comma-delimited list of FQDNs for the project") ConfigCommand.Flags().StringVar(&omitContainersArg, "omit-containers", "", "A comma-delimited list of container types that should not be started when the project is started") + ConfigCommand.Flags().StringVar(&webEnvironmentLocal, "web-environment", "", `Add environment variables to web container: --web-environment="TYPO3_CONTEXT=Development,SOMEENV=someval"`) ConfigCommand.Flags().BoolVar(&createDocroot, "create-docroot", false, "Prompts ddev to create the docroot if it doesn't exist") ConfigCommand.Flags().BoolVar(&showConfigLocation, "show-config-location", false, "Output the location of the config.yaml file if it exists, or error that it doesn't exist.") ConfigCommand.Flags().StringVar(&uploadDirArg, "upload-dir", "", "Sets the project's upload directory, the destination directory of the import-files command.") @@ -523,6 +526,15 @@ func handleMainConfigArgs(cmd *cobra.Command, args []string, app *ddevapp.DdevAp app.OmitContainers = strings.Split(omitContainersArg, ",") } + if cmd.Flag("web-environment").Changed { + env := strings.Replace(webEnvironmentLocal, " ", "", -1) + if env == "" { + app.WebEnvironment = []string{} + } else { + app.WebEnvironment = strings.Split(env, ",") + } + } + if cmd.Flag("webimage-extra-packages").Changed { if cmd.Flag("webimage-extra-packages").Value.String() == "" { app.WebImageExtraPackages = nil diff --git a/docs/users/extend/config_yaml.md b/docs/users/extend/config_yaml.md index b205cf3d6ba..28e98360972 100644 --- a/docs/users/extend/config_yaml.md +++ b/docs/users/extend/config_yaml.md @@ -29,6 +29,7 @@ the .ddev/config.yaml is the primary configuration for the project. | upload_dir | Relative path to upload directory used by `ddev import-files` | | | working_dir | explicitly specify the working directory used by `ddev exec` and `ddev ssh` | `working_dir: { web: "/var/www", db: "/etc" }` would set the working directories for the web and db containers. | | omit_containers | Allows the project to not load specified containers | For example, `omit_containers: [db, dba, ddev-ssh-agent]`. Currently only these containers are supported. Some containers can also be omitted globally in the ~/.ddev/global_config.yaml and the result is additive; all containers named in both places will be omitted. Note that if you omit the "db" container, several standard features of ddev that access the database container will be unusable. | +| web_environment | Inject environment variables into web container | For example, `web_environment: ["SOMEENV=someval", "SOMEOTHERENV=someotherval"]`. | | nfs_mount_enabled | Allows using NFS to mount the project into the container for performance reasons | See [nfs_mount_enabled documentation](../performance.md). This requires configuration on the host before it can be used. Note that project-level configuration of nfs_mount_enabled is unusual, and that if it's true in the global config, that overrides the project-specific nfs_mount_enabled| | fail_on_hook_fail | Decide whether `ddev start` should be interrupted by a failing hook | | host_https_port | Specify a specific and persistent https port for direct binding to the localhost interface | This is not commonly used, but a specific port can be provided here and the https URL will always remain the same. For example, if you put "59001", the project will always use " for the localhost URL. (Note that the named URL is more commonly used and for most purposes is better.) If this is not set the port will change from `ddev start` to `ddev start` | @@ -57,6 +58,7 @@ The $HOME/.ddev/global_config.yaml has a few key global config options. | nfs_mount_enabled | Enables NFS mounting globally for all projects | Only a "true" value has any effect. If true, NFS will be used on all projects, regardless of any settings in the individual projects. | | fail_on_hook_fail | Enables `ddev start` interruption globally for all projects when a hook fails | Decide whether `ddev start` should be interrupted by a failing hook | | omit_containers | Allows the project to not load specified containers | For example, `omit_containers: [ "dba", "ddev-ssh-agent"]`. Currently only these containers are supported. Note that you cannot omit the "db" container in the global configuration, but you can in the per-project .ddev/config.yaml. | +| web_environment | Inject environment variables into web container | For example, `web_environment: ["SOMEENV=someval", "SOMEOTHERENV=someotherval"]`. | | instrumentation_opt_in | Opt in or out of instrumentation reporting | If true, anonymous usage information is sent to ddev via [segment](https://segment.com) | | router_bind_all_interfaces | Bind on all network interfaces | If true, ddev-router will bind on all network interfaces instead of just localhost, exposing ddev projects to your local network. If you set this to true, you may consider `omit_containers: ["dba"]` so that the phpMyAdmin port is not available. | | internet_detection_timeout_ms | Internet detection timeout | ddev must detect whether the internet is working to determine whether to add hostnames to /etc/hosts. It uses a DNS query and times it out by default at 750ms. In rare cases you may need to increase this value if you have slow but working internet. See [FAQ](../faq.md) and [issue link](https://github.com/drud/ddev/issues/2409#issuecomment-662448025).| diff --git a/docs/users/extend/customization-extendibility.md b/docs/users/extend/customization-extendibility.md index ed48c47f2ab..a4f49b1eedf 100644 --- a/docs/users/extend/customization-extendibility.md +++ b/docs/users/extend/customization-extendibility.md @@ -24,22 +24,16 @@ If you need to create a service configuration for your project, see [Defining an ## Providing custom environment variables to a container -Each project can have an unlimited number of .ddev/docker-compose.\*.yaml files as described in [Custom Compose Files](./custom-compose-files.md), so it's easy to maintain custom environment variables in a .ddev/docker-compose.environment.yaml file (the exact name doesn't matter, if it just matches docker-compose.\*.yaml). - -For example, a `.ddev/docker-compose.environment.yaml` with these contents would add a $TYPO3_CONTEXT environment variable to the web container, and a $SOMETHING environment variable to the db container: +Custom environment variables may be set in the project config.yaml or the ~/.ddev/global_config.yaml with the `web_environment` key, for example ```yaml -version: '3.6' - -services: - web: - environment: - - TYPO3_CONTEXT=Development - db: - environment: - - SOMETHING=something special +web_environment: +- SOMEENV=someval +- SOMEOTHERENV=someotherval ``` +You can also use `ddev config global --web-environment="SOMEENV=someval"` or `ddev config --web-environment="SOMEENV=someval"` for the same purpose. The command just sets the values in the configuration files. + ### Providing custom nginx configuration When you `ddev start` using the `nginx-fpm` webserver_type, ddev creates a configuration customized to your project type in `.ddev/nginx_full/nginx-site.conf`. You can edit and override the configuration by removing the `#ddev-generated` line and doing whatever you need with it. After each change, `ddev start`. diff --git a/pkg/ddevapp/config.go b/pkg/ddevapp/config.go index ef07c13f315..8a302c57882 100644 --- a/pkg/ddevapp/config.go +++ b/pkg/ddevapp/config.go @@ -687,6 +687,7 @@ type composeYAMLVars struct { GID string AutoRestartContainers bool FailOnHookFail bool + WebEnvironment []string } // RenderComposeYAML renders the contents of .ddev/.ddev-docker-compose*. @@ -706,10 +707,18 @@ func (app *DdevApp) RenderComposeYAML() (string, error) { if err != nil { util.Warning("Could not determine host.docker.internal IP address: %v", err) } - // The fallthrough default for hostDockerInternalIdentifier is the // hostDockerInternalHostname == host.docker.internal + webEnvironment := globalconfig.DdevGlobalConfig.WebEnvironment + localWebEnvironment := app.WebEnvironment + for _, v := range localWebEnvironment { + // docker-compose won't accept a duplicate environment value + if !nodeps.ArrayContainsString(webEnvironment, v) { + webEnvironment = append(webEnvironment, v) + } + } + uid, gid, username := util.GetContainerUIDGid() templateVars := composeYAMLVars{ @@ -744,6 +753,7 @@ func (app *DdevApp) RenderComposeYAML() (string, error) { DBBuildDockerfile: app.GetConfigPath(".dbimageBuild/Dockerfile"), AutoRestartContainers: globalconfig.DdevGlobalConfig.AutoRestartContainers, FailOnHookFail: app.FailOnHookFail || app.FailOnHookFailGlobal, + WebEnvironment: webEnvironment, } if app.NFSMountEnabled || app.NFSMountEnabledGlobal { templateVars.MountType = "volume" diff --git a/pkg/ddevapp/ddevapp.go b/pkg/ddevapp/ddevapp.go index 83b720e4ac1..7067605d8fe 100644 --- a/pkg/ddevapp/ddevapp.go +++ b/pkg/ddevapp/ddevapp.go @@ -112,6 +112,7 @@ type DdevApp struct { Timezone string `yaml:"timezone,omitempty"` ComposerVersion string `yaml:"composer_version"` DisableSettingsManagement bool `yaml:"disable_settings_management,omitempty"` + WebEnvironment []string `yaml:"web_environment"` ComposeYaml map[string]interface{} `yaml:"-"` } diff --git a/pkg/ddevapp/templates.go b/pkg/ddevapp/templates.go index d794dfa40f0..54c43b15627 100644 --- a/pkg/ddevapp/templates.go +++ b/pkg/ddevapp/templates.go @@ -138,6 +138,8 @@ services: - SSH_AUTH_SOCK=/home/.ssh-agent/socket - TZ={{ .Timezone }} - VIRTUAL_HOST=${DDEV_HOSTNAME} + {{ range $env := .WebEnvironment }}- "{{ $env }}" + {{ end }} labels: com.ddev.site-name: ${DDEV_SITENAME} com.ddev.platform: {{ .Plugin }} @@ -347,6 +349,11 @@ const ConfigInstructions = ` # Drupal's settings.php/settings.ddev.php or TYPO3's AdditionalSettings.php # In this case the user must provide all such settings. +# You can inject environment variables into the web container with: +# web_environment: +# - SOMEENV=somevalue +# - SOMEOTHERENV=someothervalue + # no_project_mount: false # (Experimental) If true, ddev will not mount the project into the web container; # the user is responsible for mounting it manually or via a script. diff --git a/pkg/globalconfig/global_config.go b/pkg/globalconfig/global_config.go index c8332724198..50cb97a2e19 100644 --- a/pkg/globalconfig/global_config.go +++ b/pkg/globalconfig/global_config.go @@ -54,6 +54,7 @@ type GlobalConfig struct { FailOnHookFailGlobal bool `yaml:"fail_on_hook_fail"` ProjectList map[string]*ProjectInfo `yaml:"project_info"` HostDockerInternal string `yaml:"host_docker_internal"` + WebEnvironment []string `yaml:"web_environment"` } // GetGlobalConfigPath() gets the path to global config file @@ -155,6 +156,11 @@ func WriteGlobalConfig(config GlobalConfig) error { # You can enable nfs mounting for all projects with # nfs_mount_enabled: true # +# You can inject environment variables into the web container with: +# web_environment: +# - SOMEENV=somevalue +# - SOMEOTHERENV=someothervalue + # In unusual cases the default value to wait to detect internet availability is too short. # You can adjust this value higher to make it less likely that ddev will declare internet # unavailable, but ddev may wait longer on some commands. This should not be set below the default 750