File: provider.rb

package info (click to toggle)
ruby-vagrant-cloud 3.0.5-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 408 kB
  • sloc: ruby: 4,343; makefile: 7
file content (176 lines) | stat: -rw-r--r-- 5,879 bytes parent folder | download
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
175
176
module VagrantCloud
  class Box
    class Provider < Data::Mutable

      # Result for upload requests to upload directly to the
      # storage backend.
      #
      # @param [String] upload_url URL for uploading file asset
      # @param [String] callback_url URL callback to PUT after successful upload
      # @param [Proc] callback Callable proc to perform callback via configured client
      DirectUpload = Struct.new(:upload_url, :callback_url, :callback, keyword_init: true)

      attr_reader :version
      attr_required :name
      attr_optional :hosted, :created_at, :updated_at,
        :checksum, :checksum_type, :original_url, :download_url,
        :url

      attr_mutable :url, :checksum, :checksum_type

      def initialize(version:, **opts)
        if !version.is_a?(Version)
          raise TypeError, "Expecting type `#{Version.name}` but received `#{version.class.name}`"
        end
        @version = version
        super(**opts)
      end

      # Delete this provider
      #
      # @return [nil]
      def delete
        if exist?
          version.box.organization.account.client.box_version_provider_delete(
            username: version.box.username,
            name: version.box.name,
            version: version.version,
            provider: name
          )
          pv = version.providers.dup
          pv.delete(self)
          version.clean(data: {providers: pv})
        end
        nil
      end

      # Upload box file to be hosted on VagrantCloud. This
      # method provides different behaviors based on the
      # parameters passed. When the `direct` option is enabled
      # the upload target will be directly to the backend
      # storage. However, when the `direct` option is used the
      # upload process becomes a two steps where a callback
      # must be called after the upload is complete.
      #
      # If the path is provided, the file will be uploaded
      # and the callback will be requested if the `direct`
      # option is enabled.
      #
      # If a block is provided, the upload URL will be yielded
      # to the block. If the `direct` option is set, the callback
      # will be automatically requested after the block execution
      # has completed.
      #
      # If no path or block is provided, the upload URL will
      # be returned. If the `direct` option is set, the
      # `DirectUpload` instance will be yielded and it is
      # the caller's responsibility to issue the callback
      #
      # @param [String] path Path to asset
      # @param [Boolean] direct Upload directly to backend storage
      # @yieldparam [String] url URL to upload asset
      # @return [self, Object, String, DirectUpload] self when path provided, result of yield when block provided, URL otherwise
      # @note The callback request uses PUT request method
      def upload(path: nil, direct: false)
        if !exist?
          raise Error::BoxError::ProviderNotFoundError,
            "Provider #{name} not found for box #{version.box.tag} version #{version.version}"
        end
        if path && block_given?
          raise ArgumentError,
            "Only path or block may be provided, not both"
        end
        if path && !File.exist?(path)
          raise Errno::ENOENT, path
        end
        req_args = {
          username: version.box.username,
          name: version.box.name,
          version: version.version,
          provider: name
        }
        if direct
          r = version.box.organization.account.client.box_version_provider_upload_direct(**req_args)
        else
          r = version.box.organization.account.client.box_version_provider_upload(**req_args)
        end
        result = DirectUpload.new(
          upload_url: r[:upload_path],
          callback_url: r[:callback],
          callback: proc {
            if r[:callback]
              version.box.organization.account.client.
                request(method: :put, path: URI.parse(r[:callback]).path)
            end
          }
        )
        if block_given?
          block_r = yield result.upload_url
          result[:callback].call
          block_r
        elsif path
          File.open(path, "rb") do |file|
            chunks = lambda { file.read(Excon.defaults[:chunk_size]).to_s }
            Excon.put(result.upload_url, request_block: chunks)
          end
          result[:callback].call
          self
        else
          # When returning upload information for requester to complete,
          # return upload URL when `direct` option is false, otherwise
          # return the `DirectUpload` instance
          direct ? result : result.upload_url
        end
      end

      # @return [Boolean] provider exists remotely
      def exist?
        !!created_at
      end

      # Check if this instance is dirty
      #
      # @param [Boolean] deep Check nested instances
      # @return [Boolean] instance is dirty
      def dirty?(key=nil, **args)
        if key
          super(key)
        else
          super || !exist?
        end
      end

      # Save the provider if any changes have been made
      #
      # @return [self]
      def save
        save_provider if dirty?
        self
      end

      protected

      # Save the provider
      #
      # @return [self]
      def save_provider
        req_args = {
          username: version.box.username,
          name: version.box.name,
          version: version.version,
          provider: name,
          checksum: checksum,
          checksum_type: checksum_type,
          url: url
        }
        if exist?
          result = version.box.organization.account.client.box_version_provider_update(**req_args)
        else
          result = version.box.organization.account.client.box_version_provider_create(**req_args)
        end
        clean(data: result)
        self
      end
    end
  end
end