summaryrefslogtreecommitdiff
path: root/lib/leap_cli/leapfile.rb
blob: e526703d88544a59a4aaf8901d37df303e63c236 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#
# The Leapfile is the bootstrap configuration file for a LEAP provider.
#
# It is akin to a Gemfile, Rakefile, or Capfile (e.g. it is a ruby file that gets eval'ed)
#
# Additional configuration options are defined in platform's leapfile_extensions.rb
#

module LeapCli
  def self.leapfile
    @leapfile ||= Leapfile.new
  end

  class Leapfile
    attr_reader :platform_directory_path
    attr_reader :provider_directory_path
    attr_reader :environment
    attr_reader :valid

    def initialize
    end

    #
    # The way the Leapfile handles pinning of environment (self.environment) is a little tricky.
    # If self.environment is nil, then there is no pin. If self.environment is 'default', then
    # there is a pin to the default environment. The problem is that an environment of nil
    # is used to indicate the default environment in node properties.
    #
    # This method returns the environment tag as needed when filtering nodes.
    #
    def environment_filter
      if self.environment == 'default'
        nil
      else
        self.environment
      end
    end

    def load(search_directory=nil)
      directory = File.expand_path(find_in_directory_tree('Leapfile', search_directory))
      if directory == '/'
        return nil
      else
        #
        # set up paths
        #
        @provider_directory_path = directory
        begin
          # load leaprc first, so that we can potentially access which environment is pinned in Leapfile
          # but also load leaprc last, so that it can override what is set in Leapfile.
          read_settings(leaprc_path)
        rescue StandardError
        end
        read_settings(directory + '/Leapfile')
        read_settings(leaprc_path)
        @platform_directory_path = File.expand_path(@platform_directory_path || '../leap_platform', @provider_directory_path)

        #
        # load the platform
        #
        platform_class = "#{@platform_directory_path}/lib/leap/platform"
        platform_definition = "#{@platform_directory_path}/platform.rb"
        unless File.exist?(platform_definition)
          Util.bail! "ERROR: The file `#{platform_file}` does not exist. Please check the value of `@platform_directory_path` in `Leapfile` or `~/.leaprc`."
        end
        begin
          require platform_class
          require platform_definition
        rescue LoadError
          Util.log "The leap_platform at #{platform_directory_path} is not compatible with this version of leap_cli"
          Util.log "You can either:" do
            Util.log "Upgrade leap_platform to version " + LeapCli::COMPATIBLE_PLATFORM_VERSION.first
            Util.log "Or, downgrade leap_cli to version 1.8"
          end
          Util.bail!
        rescue StandardError => exc
          Util.bail! exc.to_s
        end
        begin
          Leap::Platform.validate!(LeapCli::VERSION, LeapCli::COMPATIBLE_PLATFORM_VERSION, self)
        rescue StandardError => exc
          Util.bail! exc.to_s
        end
        leapfile_extensions = "#{@platform_directory_path}/lib/leap_cli/leapfile_extensions.rb"
        if File.exist?(leapfile_extensions)
          require leapfile_extensions
        end

        #
        # validate
        #
        instance_variables.each do |var|
          var = var.to_s.sub('@', '')
          if !self.respond_to?(var)
            LeapCli.log :warning, "the variable `#{var}` is set in .leaprc or Leapfile, but it is not supported."
          end
        end
        @valid = validate
        return @valid
      end
    end

    def set(property, value)
      edit_leaprc(property, value)
    end

    def unset(property)
      edit_leaprc(property)
    end

    def valid?
      !!@valid
    end

    private

    #
    # adds or removes a line to .leaprc for this particular provider directory.
    # if value is nil, the line is removed. if not nil, it is added or replaced.
    #
    def edit_leaprc(property, value=nil)
      file_path = leaprc_path
      lines = []
      if File.exist?(file_path)
        regexp = /self\.#{Regexp.escape(property)} = .*? if @provider_directory_path == '#{Regexp.escape(@provider_directory_path)}'/
        File.readlines(file_path).each do |line|
          unless line =~ regexp
            lines << line
          end
        end
      end
      unless value.nil?
        lines << "self.#{property} = #{value.inspect} if @provider_directory_path == '#{@provider_directory_path}'\n"
      end
      File.open(file_path, 'w') do |f|
        f.write(lines.join)
      end
    rescue Errno::EACCES, IOError => exc
      Util::bail! :error, "trying to save ~/.leaprc (#{exc})."
    end

    def leaprc_path
      File.join(ENV['HOME'], '.leaprc')
    end

    def read_settings(file)
      if File.exist? file
        LeapCli.log 2, :read, file
        instance_eval(File.read(file), file)
      end
    end

    def find_in_directory_tree(filename, directory_tree=nil)
      search_dir = directory_tree || Dir.pwd
      while search_dir != "/"
        Dir.foreach(search_dir) do |f|
          return search_dir if f == filename
        end
        search_dir = File.dirname(search_dir)
      end
      return search_dir
    end

    def method_missing(method, *args)
      if method =~ /=$/
        self.instance_variable_set('@' + method.to_s.sub('=',''), args.first)
      else
        self.instance_variable_get('@' + method.to_s)
      end
    end

  end
end