diff --git a/plugins/pushes/ftp/config.rb b/plugins/pushes/ftp/config.rb new file mode 100644 index 000000000..7ee1961a4 --- /dev/null +++ b/plugins/pushes/ftp/config.rb @@ -0,0 +1,128 @@ +module VagrantPlugins + module FTPPush + class Config < Vagrant.plugin("2", :config) + # The (S)FTP host to use. + # @return [String] + attr_accessor :host + + # The username to use for authentication with the (S)FTP server. + # @return [String] + attr_accessor :username + + # The password to use for authentication with the (S)FTP server. + # @return [String] + attr_accessor :password + + # Use passive FTP (default is true). + # @return [true, false] + attr_accessor :passive + + # Use secure (SFTP) (default is false). + # @return [true, false] + attr_accessor :secure + + # The root destination on the target system to sync the files (default is + # /). + # @return [String] + attr_accessor :destination + + # Lists of files to include/exclude in what is uploaded. Exclude is + # always the last run filter, so if a file is matched in both include + # and exclude, it will be excluded. + # + # The value of the array elements should be a simple file glob relative + # to the directory being packaged. + # @return [Array] + attr_accessor :includes + attr_accessor :excludes + + # The base directory with file contents to upload. By default this + # is the same directory as the Vagrantfile, but you can specify this + # if you have a `src` folder or `bin` folder or some other folder + # you want to upload. + # @return [String] + attr_accessor :dir + + def initialize + @host = UNSET_VALUE + @username = UNSET_VALUE + @password = UNSET_VALUE + @passive = UNSET_VALUE + @secure = UNSET_VALUE + @destination = UNSET_VALUE + + @includes = [] + @excludes = [] + + @dir = UNSET_VALUE + end + + def merge(other) + super.tap do |result| + result.includes = self.includes.dup.concat(other.includes).uniq + result.excludes = self.excludes.dup.concat(other.excludes).uniq + end + end + + def finalize! + @host = nil if @host == UNSET_VALUE + @username = nil if @username == UNSET_VALUE + @password = nil if @password == UNSET_VALUE + @passive = true if @passive == UNSET_VALUE + @secure = false if @secure == UNSET_VALUE + @destination = "/" if @destination == UNSET_VALUE + @dir = "." if @dir == UNSET_VALUE + end + + def validate(machine) + errors = _detected_errors + + if missing?(@host) + errors << I18n.t("ftp_push.errors.missing_attribute", + attribute: "host", + ) + end + + if missing?(@username) + errors << I18n.t("ftp_push.errors.missing_attribute", + attribute: "username", + ) + end + + if missing?(@destination) + errors << I18n.t("ftp_push.errors.missing_attribute", + attribute: "destination", + ) + end + + if missing?(@dir) + errors << I18n.t("ftp_push.errors.missing_attribute", + attribute: "dir", + ) + end + + { "FTP push" => errors } + end + + # Add the filepath to the list of includes + # @param [String] filepath + def include(filepath) + @includes << filepath + end + + # Add the filepath to the list of excludes + # @param [String] filepath + def exclude(filepath) + @excludes << filepath + end + + private + + # Determine if the given string is "missing" (blank) + # @return [true, false] + def missing?(obj) + obj.to_s.strip.empty? + end + end + end +end diff --git a/plugins/pushes/ftp/locales/en.yml b/plugins/pushes/ftp/locales/en.yml new file mode 100644 index 000000000..dcac3a427 --- /dev/null +++ b/plugins/pushes/ftp/locales/en.yml @@ -0,0 +1,9 @@ +en: + ftp_push: + errors: + missing_attribute: |- + Missing required attribute '%{attribute}'. The Vagrant FTP Push plugin + requires you set this attribute. Please set this attribute in your + Vagrantfile, for example: + + push.%{attribute} = "..." diff --git a/test/unit/plugins/pushes/ftp/config_test.rb b/test/unit/plugins/pushes/ftp/config_test.rb new file mode 100644 index 000000000..ef5010395 --- /dev/null +++ b/test/unit/plugins/pushes/ftp/config_test.rb @@ -0,0 +1,171 @@ +require_relative "../../../base" + +require Vagrant.source_root.join("plugins/pushes/ftp/config") + +describe VagrantPlugins::FTPPush::Config do + include_context "unit" + + before(:all) do + I18n.load_path << Vagrant.source_root.join("plugins/pushes/ftp/locales/en.yml") + I18n.reload! + end + + subject { described_class.new } + + let(:machine) { double("machine") } + + describe "#host" do + it "defaults to nil" do + subject.finalize! + expect(subject.host).to be(nil) + end + end + + describe "#username" do + it "defaults to nil" do + subject.finalize! + expect(subject.username).to be(nil) + end + end + + describe "#password" do + it "defaults to nil" do + subject.finalize! + expect(subject.password).to be(nil) + end + end + + describe "#passive" do + it "defaults to true" do + subject.finalize! + expect(subject.passive).to be(true) + end + end + + describe "#secure" do + it "defaults to false" do + subject.finalize! + expect(subject.secure).to be(false) + end + end + + describe "#destination" do + it "defaults to /" do + subject.finalize! + expect(subject.destination).to eq("/") + end + end + + describe "#dir" do + it "defaults to nil" do + subject.finalize! + expect(subject.dir).to eq(".") + end + end + + describe "#merge" do + context "when includes are given" do + let(:one) { described_class.new } + let(:two) { described_class.new } + + it "merges the result" do + one.includes = %w(a b c) + two.includes = %w(c d e) + result = one.merge(two) + expect(result.includes).to eq(%w(a b c d e)) + end + end + + context "when excludes are given" do + let(:one) { described_class.new } + let(:two) { described_class.new } + + it "merges the result" do + one.excludes = %w(a b c) + two.excludes = %w(c d e) + result = one.merge(two) + expect(result.excludes).to eq(%w(a b c d e)) + end + end + end + + describe "#validate" do + before do + allow(machine).to receive(:env) + .and_return(double("env", + root_path: "", + )) + + subject.host = "ftp.example.com" + subject.username = "sethvargo" + subject.password = "bacon" + subject.destination = "/" + subject.dir = "." + end + + let(:result) { subject.validate(machine) } + let(:errors) { result["FTP push"] } + + context "when the host is missing" do + it "returns an error" do + subject.host = "" + subject.finalize! + expect(errors).to include(I18n.t("ftp_push.errors.missing_attribute", + attribute: "host", + )) + end + end + + context "when the username is missing" do + it "returns an error" do + subject.username = "" + subject.finalize! + expect(errors).to include(I18n.t("ftp_push.errors.missing_attribute", + attribute: "username", + )) + end + end + + context "when the password is missing" do + it "does not return an error" do + subject.password = "" + subject.finalize! + expect(errors).to be_empty + end + end + + context "when the destination is missing" do + it "returns an error" do + subject.destination = "" + subject.finalize! + expect(errors).to include(I18n.t("ftp_push.errors.missing_attribute", + attribute: "destination", + )) + end + end + + context "when the dir is missing" do + it "returns an error" do + subject.dir = "" + subject.finalize! + expect(errors).to include(I18n.t("ftp_push.errors.missing_attribute", + attribute: "dir", + )) + end + end + end + + describe "#include" do + it "adds the item to the list" do + subject.include("me") + expect(subject.includes).to include("me") + end + end + + describe "#exclude" do + it "adds the item to the list" do + subject.exclude("not me") + expect(subject.excludes).to include("not me") + end + end +end