@@ -16,29 +16,88 @@ class << self
1616 attr_reader :registry
1717
1818 def register ( provider , klass )
19- registry [ provider ] = klass
19+ registry [ provider . to_s ] = klass
2020 end
2121
22- def build ( connection_config :, directory_key :, root_dir :, min_size : nil , max_size : nil )
23- provider = connection_config [ :provider ]
24- raise 'Missing connection_config[:provider]' if provider . nil?
22+ def build ( directory_key :, root_dir :, resource_type : nil , min_size : nil , max_size : nil )
23+ raise 'Missing resource_type' if resource_type . nil?
2524
26- impl_class = registry [ provider ]
25+ cfg = fetch_config ( resource_type )
26+ provider = cfg [ 'provider' ]
27+
28+ key = provider . to_s
29+ impl_class = registry [ key ] || registry [ key . downcase ] || registry [ key . upcase ]
2730 raise "No storage CLI client registered for provider #{ provider } " unless impl_class
2831
29- impl_class . new ( connection_config :, directory_key :, root_dir :, min_size :, max_size :)
32+ impl_class . new ( provider : provider , directory_key : directory_key , root_dir : root_dir , resource_type : resource_type , min_size : min_size , max_size : max_size ,
33+ config_path : config_path_for ( resource_type ) )
34+ end
35+
36+ RESOURCE_TYPE_KEYS = {
37+ 'droplets' => :storage_cli_config_file_droplets ,
38+ 'buildpack_cache' => :storage_cli_config_file_droplets ,
39+ 'buildpacks' => :storage_cli_config_file_buildpacks ,
40+ 'packages' => :storage_cli_config_file_packages ,
41+ 'resource_pool' => :storage_cli_config_file_resource_pool
42+ } . freeze
43+
44+ def fetch_config ( resource_type )
45+ path = config_path_for ( resource_type )
46+ validate_config_path! ( path )
47+
48+ json = fetch_json ( path )
49+ validate_json_object! ( json , path )
50+ validate_required_keys! ( json , path )
51+
52+ json
53+ end
54+
55+ def config_path_for ( resource_type )
56+ normalized = resource_type . to_s
57+ key = RESOURCE_TYPE_KEYS . fetch ( normalized ) do
58+ raise BlobstoreError . new ( "Unknown resource_type: #{ resource_type } " )
59+ end
60+ VCAP ::CloudController ::Config . config . get ( key )
61+ end
62+
63+ def fetch_json ( path )
64+ Oj . load ( File . read ( path ) )
65+ rescue Oj ::ParseError , EncodingError => e
66+ raise BlobstoreError . new ( "Failed to parse storage-cli JSON at #{ path } : #{ e . message } " )
67+ end
68+
69+ def validate_config_path! ( path )
70+ return if path && File . file? ( path ) && File . readable? ( path )
71+
72+ raise BlobstoreError . new ( "Storage-cli config file not found or not readable at: #{ path . inspect } " )
73+ end
74+
75+ def validate_json_object! ( json , path )
76+ raise BlobstoreError . new ( "Config at #{ path } must be a JSON object" ) unless json . is_a? ( Hash )
77+ end
78+
79+ def validate_required_keys! ( json , path )
80+ provider = json [ 'provider' ] . to_s . strip
81+ raise BlobstoreError . new ( "No provider specified in config file: #{ path . inspect } " ) if provider . empty?
82+
83+ required = %w[ account_key account_name container_name environment ]
84+ missing = required . reject { |k | json . key? ( k ) && !json [ k ] . to_s . strip . empty? }
85+ return if missing . empty?
86+
87+ raise BlobstoreError . new ( "Missing required keys in #{ path } : #{ missing . join ( ', ' ) } " )
3088 end
3189 end
3290
33- def initialize ( connection_config :, directory_key :, root_dir :, min_size : nil , max_size : nil )
91+ def initialize ( provider :, directory_key :, resource_type : , root_dir : , config_path :, min_size : nil , max_size : nil )
3492 @cli_path = cli_path
3593 @directory_key = directory_key
94+ @resource_type = resource_type . to_s
3695 @root_dir = root_dir
3796 @min_size = min_size || 0
3897 @max_size = max_size
39- config = build_config ( connection_config )
40- @config_file = write_config_file ( config )
41- @fork = connection_config . fetch ( :fork , false )
98+ @provider = provider
99+ @config_file = config_path
100+ logger . info ( 'storage_cli_config_selected' , resource_type : @resource_type , path : @config_file )
42101 end
43102
44103 def local?
@@ -88,28 +147,16 @@ def cp_to_blobstore(source_path, destination_key)
88147 end
89148
90149 def cp_file_between_keys ( source_key , destination_key )
91- if @fork
92- run_cli ( 'copy' , partitioned_key ( source_key ) , partitioned_key ( destination_key ) )
93- else
94- # Azure CLI doesn't support server-side copy yet, so fallback to local copy
95- Tempfile . create ( 'blob-copy' ) do |tmp |
96- download_from_blobstore ( source_key , tmp . path )
97- cp_to_blobstore ( tmp . path , destination_key )
98- end
99- end
150+ run_cli ( 'copy' , partitioned_key ( source_key ) , partitioned_key ( destination_key ) )
100151 end
101152
102153 def delete_all ( _ = nil )
103154 # page_size is currently not considered. Azure SDK / API has a limit of 5000
104- pass unless @fork
105-
106155 # Currently, storage-cli does not support bulk deletion.
107156 run_cli ( 'delete-recursive' , @root_dir )
108157 end
109158
110159 def delete_all_in_path ( path )
111- pass unless @fork
112-
113160 # Currently, storage-cli does not support bulk deletion.
114161 run_cli ( 'delete-recursive' , partitioned_key ( path ) )
115162 end
@@ -123,29 +170,19 @@ def delete_blob(blob)
123170 end
124171
125172 def blob ( key )
126- if @fork
127- properties = properties ( key )
128- return nil if properties . nil? || properties . empty?
129-
130- signed_url = sign_url ( partitioned_key ( key ) , verb : 'get' , expires_in_seconds : 3600 )
131- StorageCliBlob . new ( key , properties :, signed_url :)
132- elsif exists? ( key )
133- # Azure CLI does not support getting blob properties directly, so fallback to local check
134- signed_url = sign_url ( partitioned_key ( key ) , verb : 'get' , expires_in_seconds : 3600 )
135- StorageCliBlob . new ( key , signed_url :)
136- end
173+ properties = properties ( key )
174+ return nil if properties . nil? || properties . empty?
175+
176+ signed_url = sign_url ( partitioned_key ( key ) , verb : 'get' , expires_in_seconds : 3600 )
177+ StorageCliBlob . new ( key , properties :, signed_url :)
137178 end
138179
139180 def files_for ( prefix , _ignored_directory_prefixes = [ ] )
140- return nil unless @fork
141-
142181 files , _status = run_cli ( 'list' , prefix )
143182 files . split ( "\n " ) . map ( &:strip ) . reject ( &:empty? ) . map { |file | StorageCliBlob . new ( file ) }
144183 end
145184
146185 def ensure_bucket_exists
147- return unless @fork
148-
149186 run_cli ( 'ensure-bucket-exists' )
150187 end
151188
@@ -194,18 +231,6 @@ def build_config(connection_config)
194231 raise NotImplementedError
195232 end
196233
197- def write_config_file ( config )
198- # TODO: Consider to move the config generation into capi-release
199- config_dir = File . join ( tmpdir , 'blobstore-configs' )
200- FileUtils . mkdir_p ( config_dir )
201-
202- config_file_path = File . join ( config_dir , "#{ @directory_key } .json" )
203- File . open ( config_file_path , 'w' , 0o600 ) do |f |
204- f . write ( Oj . dump ( config . transform_keys ( &:to_s ) ) )
205- end
206- config_file_path
207- end
208-
209234 def tmpdir
210235 VCAP ::CloudController ::Config . config . get ( :directories , :tmpdir )
211236 rescue StandardError
0 commit comments