require "pathname" require_relative "../../../lib/vagrant/util/platform" module VagrantPlugins module DockerProvider class Config < Vagrant.plugin("2", :config) attr_accessor :image, :cmd, :ports, :volumes, :privileged # Additional arguments to pass to `docker build` when creating # an image using the build dir setting. # # @return [Array] attr_accessor :build_args # The directory with a Dockerfile to build and use as the basis # for this container. If this is set, neither "image" nor "git_repo" # should be set. # # @return [String] attr_accessor :build_dir # The URL for a git repository with a Dockerfile to build and use # as the basis for this container. If this is set, neither "image" # nor "build_dir" should be set. # # @return [String] attr_accessor :git_repo # Use docker-compose to manage the lifecycle and environment for # containers instead of using docker directly. # # @return [Boolean] attr_accessor :compose # Configuration Hash used for build the docker-compose composition # file. This can be used for adding networks or volumes. # # @return [Hash] attr_accessor :compose_configuration # An optional file name of a Dockerfile to be used when building # the image. This requires Docker >1.5.0. # # @return [String] attr_accessor :dockerfile # Additional arguments to pass to `docker run` when creating # the container for the first time. This is an array of args. # # @return [Array] attr_accessor :create_args # Environmental variables to set in the container. # # @return [Hash] attr_accessor :env # Ports to expose from the container but not to the host machine. # This is useful for links. # # @return [Array] attr_accessor :expose # Force using a proxy VM, even on Linux hosts. # # @return [Boolean] attr_accessor :force_host_vm # True if the Docker container exposes SSH access. If this is true, # then Vagrant can do a bunch more things like setting the hostname, # provisioning, etc. attr_accessor :has_ssh # Options for the build dir synced folder if a host VM is in use. # # @return [Hash] attr_accessor :host_vm_build_dir_options # The name for the container. This must be unique for all containers # on the proxy machine if it is made. # # @return [String] attr_accessor :name # If true, the image will be pulled on every `up` and `reload` # to ensure the latest image. # # @return [Bool] attr_accessor :pull # True if the docker container is meant to stay in the "running" # state (is a long running process). By default this is true. # # @return [Boolean] attr_accessor :remains_running # The time to wait before sending a SIGTERM to the container # when it is stopped. # # @return [Integer] attr_accessor :stop_timeout # The name of the machine in the Vagrantfile set with # "vagrant_vagrantfile" that will be the docker host. Defaults # to "default" # # See the "vagrant_vagrantfile" docs for more info. # # @return [String] attr_accessor :vagrant_machine # The path to the Vagrantfile that contains a VM that will be # started as the Docker host if needed (Windows, OS X, Linux # without container support). # # Defaults to a built-in Vagrantfile that will load boot2docker. # # NOTE: This only has an effect if Vagrant needs a Docker host. # Vagrant determines this automatically based on the environment # it is running in. # # @return [String] attr_accessor :vagrant_vagrantfile #-------------------------------------------------------------- # Auth Settings #-------------------------------------------------------------- # Server to authenticate to. If blank, will use the default # Docker authentication endpoint (which is the Docker Hub at the # time of this comment). # # @return [String] attr_accessor :auth_server # Email for logging in to a remote Docker server. # # @return [String] attr_accessor :email # Email for logging in to a remote Docker server. # # @return [String] attr_accessor :username # Password for logging in to a remote Docker server. If this is # not blank, then Vagrant will run `docker login` prior to any # Docker runs. # # The presence of auth will also force the Docker environments to # serialize on `up` so that different users/passwords don't overlap. # # @return [String] attr_accessor :password def initialize @build_args = [] @build_dir = UNSET_VALUE @git_repo = UNSET_VALUE @cmd = UNSET_VALUE @compose = UNSET_VALUE @compose_configuration = {} @create_args = UNSET_VALUE @dockerfile = UNSET_VALUE @env = {} @expose = [] @force_host_vm = UNSET_VALUE @has_ssh = UNSET_VALUE @host_vm_build_dir_options = UNSET_VALUE @image = UNSET_VALUE @name = UNSET_VALUE @links = [] @pull = UNSET_VALUE @ports = UNSET_VALUE @privileged = UNSET_VALUE @remains_running = UNSET_VALUE @stop_timeout = UNSET_VALUE @volumes = [] @vagrant_machine = UNSET_VALUE @vagrant_vagrantfile = UNSET_VALUE @auth_server = UNSET_VALUE @email = UNSET_VALUE @username = UNSET_VALUE @password = UNSET_VALUE end def link(name) @links << name end def merge(other) super.tap do |result| # This is a bit confusing. The tests explain the purpose of this # better than the code lets on, I believe. has_image = (other.image != UNSET_VALUE) has_build_dir = (other.build_dir != UNSET_VALUE) has_git_repo = (other.git_repo != UNSET_VALUE) if (has_image ^ has_build_dir ^ has_git_repo) && !(has_image && has_build_dir && has_git_repo) # image if has_image if @build_dir != UNSET_VALUE result.build_dir = nil end if @git_repo != UNSET_VALUE result.git_repo = nil end end # build_dir if has_build_dir if @image != UNSET_VALUE result.image = nil end if @git_repo != UNSET_VALUE result.git_repo = nil end end # git_repo if has_git_repo if @build_dir != UNSET_VALUE result.build_dir = nil end if @image != UNSET_VALUE result.image = nil end end end env = {} env.merge!(@env) if @env env.merge!(other.env) if other.env result.env = env expose = self.expose.dup expose += other.expose result.instance_variable_set(:@expose, expose) links = _links.dup links += other._links result.instance_variable_set(:@links, links) end end def finalize! @build_args = [] if @build_args == UNSET_VALUE @build_dir = nil if @build_dir == UNSET_VALUE @git_repo = nil if @git_repo == UNSET_VALUE @cmd = [] if @cmd == UNSET_VALUE @compose = false if @compose == UNSET_VALUE @create_args = [] if @create_args == UNSET_VALUE @dockerfile = nil if @dockerfile == UNSET_VALUE @env ||= {} @has_ssh = false if @has_ssh == UNSET_VALUE @image = nil if @image == UNSET_VALUE @name = nil if @name == UNSET_VALUE @pull = false if @pull == UNSET_VALUE @ports = [] if @ports == UNSET_VALUE @privileged = false if @privileged == UNSET_VALUE @remains_running = true if @remains_running == UNSET_VALUE @stop_timeout = 1 if @stop_timeout == UNSET_VALUE @vagrant_machine = nil if @vagrant_machine == UNSET_VALUE @vagrant_vagrantfile = nil if @vagrant_vagrantfile == UNSET_VALUE @auth_server = nil if @auth_server == UNSET_VALUE @email = "" if @email == UNSET_VALUE @username = "" if @username == UNSET_VALUE @password = "" if @password == UNSET_VALUE if @host_vm_build_dir_options == UNSET_VALUE @host_vm_build_dir_options = nil end # On non-linux platforms (where there is no native docker), force the # host VM. Other users can optionally disable this by setting the # value explicitly to false in their Vagrantfile. if @force_host_vm == UNSET_VALUE @force_host_vm = !Vagrant::Util::Platform.linux? && !Vagrant::Util::Platform.darwin? && !Vagrant::Util::Platform.windows? end # The machine name must be a symbol @vagrant_machine = @vagrant_machine.to_sym if @vagrant_machine @expose.uniq! if @compose_configuration.is_a?(Hash) # Ensures configuration is using basic types @compose_configuration = JSON.parse(@compose_configuration.to_json) end end def validate(machine) errors = _detected_errors # FIXME: is there a more ruby-elegant way to write this? if (@build_dir? 1 : 0) + (@git_repo? 1 : 0) + (@image? 1 : 0) > 1 errors << I18n.t("docker_provider.errors.config.both_build_and_image") end if !@build_dir && !@git_repo && !@image errors << I18n.t("docker_provider.errors.config.build_dir_or_image") end if @build_dir build_dir_pn = Pathname.new(@build_dir) if !build_dir_pn.directory? errors << I18n.t("docker_provider.errors.config.build_dir_invalid") end end # Comparison logic taken directly from docker's urlutil.go if @git_repo && !( @git_repo =~ /^http(?:s)?:\/\/.*.git(?:#.+)?$/ || @git_repo =~ /^git(?:hub\.com|@|:\/\/)/) errors << I18n.t("docker_provider.errors.config.git_repo_invalid") end if !@compose_configuration.is_a?(Hash) errors << I18n.t("docker_provider.errors.config.compose_configuration_hash") end if @compose && @force_host_vm errors << I18n.t("docker_provider.errors.config.compose_force_vm") end if !@create_args.is_a?(Array) errors << I18n.t("docker_provider.errors.config.create_args_array") end @links.each do |link| parts = link.split(":") if parts.length != 2 || parts[0] == "" || parts[1] == "" errors << I18n.t( "docker_provider.errors.config.invalid_link", link: link) end end if @vagrant_vagrantfile vf_pn = Pathname.new(@vagrant_vagrantfile) if !vf_pn.file? errors << I18n.t("docker_provider.errors.config.invalid_vagrantfile") end end { "docker provider" => errors } end #-------------------------------------------------------------- # Functions below should not be called by config files #-------------------------------------------------------------- def _links @links end end end end