File: state_file.rb

package info (click to toggle)
vagrant 2.3.7%2Bgit20230731.5fc64cde%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 17,616 kB
  • sloc: ruby: 111,820; sh: 462; makefile: 123; ansic: 34; lisp: 1
file content (153 lines) | stat: -rw-r--r-- 4,243 bytes parent folder | download | duplicates (5)
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
require "json"
require "fileutils"
require "tempfile"

module Vagrant
  module Plugin
    # This is a helper to deal with the plugin state file that Vagrant
    # uses to track what plugins are installed and activated and such.
    class StateFile

      # @return [Pathname] path to file
      attr_reader :path

      def initialize(path, system = false)
        @path = path
        @system = system

        @data = {}
        if @path.exist?
          begin
            @data = JSON.parse(@path.read)
          rescue JSON::ParserError => e
            raise Vagrant::Errors::PluginStateFileParseError,
              path: path, message: e.message
          end

          upgrade_v0! if !@data["version"]
        end

        @data["version"] ||= "1"
        @data["installed"] ||= {}
        load_extra_plugins
      end

      def load_extra_plugins
        extra_plugins = Dir.glob(@path.dirname.join('plugins.d', '*.json'))
        extra_plugins.each do |filename|
          json = File.read(filename)
          begin
            plugin_data = JSON.parse(json)
            @data["installed"].merge!(plugin_data)
          rescue JSON::ParserError => e
            raise Vagrant::Errors::PluginStateFileParseError,
              path: filename, message: e.message
          end
        end
      end

      # Add a plugin that is installed to the state file.
      #
      # @param [String] name The name of the plugin
      def add_plugin(name, **opts)
        @data["installed"][name] = {
          "ruby_version"          => RUBY_VERSION,
          "vagrant_version"       => Vagrant::VERSION,
          "gem_version"           => opts[:version] || "",
          "require"               => opts[:require] || "",
          "sources"               => opts[:sources] || [],
          "installed_gem_version" => opts[:installed_gem_version],
          "env_local"             => !!opts[:env_local]
        }

        save!
      end

      # Adds a RubyGems index source to look up gems.
      #
      # @param [String] url URL of the source.
      def add_source(url)
        @data["sources"] ||= []
        @data["sources"] << url if !@data["sources"].include?(url)
        save!
      end

      # This returns a hash of installed plugins according to the state
      # file. Note that this may _not_ directly match over to actually
      # installed gems.
      #
      # @return [Hash]
      def installed_plugins
        @data["installed"]
      end

      # Returns true/false if the plugin is present in this state file.
      #
      # @return [Boolean]
      def has_plugin?(name)
        @data["installed"].key?(name)
      end

      # Remove a plugin that is installed from the state file.
      #
      # @param [String] name The name of the plugin.
      def remove_plugin(name)
        @data["installed"].delete(name)
        save!
      end

      # Remove a source for RubyGems.
      #
      # @param [String] url URL of the source
      def remove_source(url)
        @data["sources"] ||= []
        @data["sources"].delete(url)
        save!
      end

      # Returns the list of RubyGems sources that will be searched for
      # plugins.
      #
      # @return [Array<String>]
      def sources
        @data["sources"] || []
      end

      # This saves the state back into the state file.
      def save!
        Tempfile.open(@path.basename.to_s, @path.dirname.to_s) do |f|
          f.binmode
          f.write(JSON.dump(@data))
          f.fsync
          f.chmod(0644)
          f.close
          FileUtils.mv(f.path, @path)
        end
      rescue Errno::EACCES
        # Ignore permission denied against system-installed plugins; regular
        # users are not supposed to write there.
        raise unless @system
      end

      protected

      # This upgrades the internal data representation from V0 (the initial
      # version) to V1.
      def upgrade_v0!
        @data["version"] = "1"

        new_installed = {}
        (@data["installed"] || []).each do |plugin|
          new_installed[plugin] = {
            "ruby_version"    => "0",
            "vagrant_version" => "0",
          }
        end

        @data["installed"] = new_installed

        save!
      end
    end
  end
end