azure-blob 0.5.0 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 94edcaaaa7717925c1fb8238b9e7420949b26c98c90344cbf54930d5e0ff3b19
4
- data.tar.gz: aae153da753212659590ed4a3ae029c86cf7ed66b4f924dac1a5f3a5ff3b2e05
3
+ metadata.gz: 72003b15ad78aeab66adc7aca2e6786ae2921ad1469cf94145e049705f52c33c
4
+ data.tar.gz: a77f87dff2f59a22476f0c5e490540a73166cf88dbf3616de8a15a87132aaa5f
5
5
  SHA512:
6
- metadata.gz: 102c3d5fd08761199413e96bd8e4bae8a84ac5478d7bb1202e232fbff346703f7cb3b8f344553983b607dbacec56942452ce18aa0afc184a4c994d31dc94118c
7
- data.tar.gz: 646636874dd381757595f82999dd0b7b5647a740512c8e2baf86bdc2045e579cdf78354a4690c746452280efb7ff92cbf9c49c95775ccbcf6a690eb0ae05131f
6
+ metadata.gz: 910c2d759b83d7b56168fd42a239ffb5da1b4aced25b8f39bc84a1beb63e736690a72586f64313d5fcebf7250808b27d15e23450e850635e24558891eed375ff
7
+ data.tar.gz: 2d4ecfb6e3e5318d8c6cb07569da7b83bccd49ee3a7d153d43c41e655474dcd43d7a73d28d68fb2baa4124326a1b41562275314348730250610420640c3dc3bd
data/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ## [Unreleased]
2
2
 
3
+
4
+ ## [0.5.2] 2024-09-12
5
+
6
+ - Add get_container_properties
7
+ - Add create_container
8
+ - Add delete_container
9
+ - Support for Azure China, US Gov and Germany
10
+
11
+ ## [0.5.1] 2024-09-09
12
+
13
+ - Remove dev files from the release
14
+
3
15
  ## [0.5.0] 2024-09-09
4
16
 
5
17
  - Added support for Managed Identities (Entra ID)
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # AzureBlob
2
2
 
3
- This gem was built to replace azure-storage-blob (deprecated) in Active Storage, but was written to be Rails agnostic.
3
+ Azure Blob client and Active Storage adapter to replace the now abandoned azure-storage-blob
4
+
5
+ An Active Storage is supplied, but the gem is Rails agnostic and can be used in any Ruby project.
4
6
 
5
7
  ## Active Storage
6
8
 
@@ -12,6 +14,16 @@ To migrate from azure-storage-blob to azure-blob:
12
14
  3. Change the `AzureStorage` service to `AzureBlob` in your Active Storage config (`config/storage.yml`)
13
15
  4. Restart or deploy the app.
14
16
 
17
+ Example config:
18
+
19
+ ```
20
+ microsoft:
21
+ service: AzureBlob
22
+ storage_account_name: account_name
23
+ storage_access_key: SECRET_KEY
24
+ container: container_name
25
+ ```
26
+
15
27
  ### Managed Identity (Entra ID)
16
28
 
17
29
  AzureBlob supports managed identities on :
@@ -22,9 +34,7 @@ AzureBlob supports managed identities on :
22
34
 
23
35
  AKS support will likely require more work. Contributions are welcome.
24
36
 
25
- To authenticate through managed identities instead of a shared key, omit `storage_access_key` from your `storage.yml` file.
26
-
27
- It is recommended to add the identity's `principal_id` to the config.
37
+ To authenticate through managed identities instead of a shared key, omit `storage_access_key` from your `storage.yml` file and pass in the identity `principal_id`.
28
38
 
29
39
  ActiveStorage config example:
30
40
 
@@ -79,11 +89,12 @@ A dev environment is supplied through Nix with [devenv](https://devenv.sh/).
79
89
 
80
90
  To test with Entra ID, the `AZURE_ACCESS_KEY` environment variable must be unset and the code must be ran or proxied through a VPS with the proper roles.
81
91
 
82
- For cost saving, the terraform variable `create_vm` is false by default.
83
- To create the VPS, Create a var file `var.tfvars` containing:
92
+ For cost saving, the terraform variable `create_vm` and `create_app_service` are false by default.
93
+ To create the VPS and App service, Create a var file `var.tfvars` containing:
84
94
 
85
95
  ```
86
96
  create_vm = true
97
+ create_app_service = true
87
98
  ```
88
99
  and re-apply terraform: `terraform apply -var-file=var.tfvars`.
89
100
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AzureBlob
4
- # AzureBlob::Blob holds the metada for a given Blob.
4
+ # AzureBlob::Blob holds the metadata for a given Blob.
5
5
  class Blob
6
6
  # You should not instanciate this object directly,
7
7
  # but obtain one when calling relevant methods of AzureBlob::Client.
@@ -32,7 +32,7 @@ module AzureBlob
32
32
  response.code == "200"
33
33
  end
34
34
 
35
- # Returns the custom Azure metada tagged on the blob.
35
+ # Returns the custom Azure metadata tagged on the blob.
36
36
  def metadata
37
37
  @metadata || response
38
38
  .to_hash
@@ -3,6 +3,7 @@
3
3
  require_relative "block_list"
4
4
  require_relative "blob_list"
5
5
  require_relative "blob"
6
+ require_relative "container"
6
7
  require_relative "http"
7
8
  require_relative "shared_key_signer"
8
9
  require_relative "entra_id_signer"
@@ -13,13 +14,22 @@ module AzureBlob
13
14
  # AzureBlob Client class. You interact with the Azure Blob api
14
15
  # through an instance of this class.
15
16
  class Client
16
- def initialize(account_name:, access_key:, container:, **options)
17
+ def initialize(account_name:, access_key: nil, principal_id: nil, container:, **options)
17
18
  @account_name = account_name
18
19
  @container = container
20
+ @cloud_regions = options[:cloud_regions]&.to_sym || :global
19
21
 
20
- @signer = !access_key.nil? && !access_key.empty? ?
21
- AzureBlob::SharedKeySigner.new(account_name:, access_key:) :
22
- AzureBlob::EntraIdSigner.new(account_name:, **options.slice(:principal_id))
22
+ no_access_key = access_key.nil? || access_key&.empty?
23
+ using_managed_identities = no_access_key && !principal_id.nil? || options[:use_managed_identities]
24
+
25
+ if !using_managed_identities && no_access_key
26
+ raise AzureBlob::Error.new(
27
+ "`access_key` cannot be empty. To use managed identities instead, pass a `principal_id` or set `use_managed_identities` to true."
28
+ )
29
+ end
30
+ @signer = using_managed_identities ?
31
+ AzureBlob::EntraIdSigner.new(account_name:, host:, principal_id: ) :
32
+ AzureBlob::SharedKeySigner.new(account_name:, access_key:)
23
33
  end
24
34
 
25
35
  # Create a blob of type block. Will automatically split the the blob in multiple block and send the blob in pieces (blocks) if the blob is too big.
@@ -138,7 +148,7 @@ module AzureBlob
138
148
  #
139
149
  # Calls to {Get Blob Properties}[https://learn.microsoft.com/en-us/rest/api/storageservices/get-blob-properties]
140
150
  #
141
- # This can be used to see if the blob exist or obtain metada such as content type, disposition, checksum or Azure custom metadata.
151
+ # This can be used to see if the blob exist or obtain metadata such as content type, disposition, checksum or Azure custom metadata.
142
152
  def get_blob_properties(key, options = {})
143
153
  uri = generate_uri("#{container}/#{key}")
144
154
 
@@ -147,6 +157,37 @@ module AzureBlob
147
157
  Blob.new(response)
148
158
  end
149
159
 
160
+ # Returns a Container object.
161
+ #
162
+ # Calls to {Get Container Properties}[https://learn.microsoft.com/en-us/rest/api/storageservices/get-container-properties]
163
+ #
164
+ # This can be used to see if the container exist or obtain metadata.
165
+ def get_container_properties(options = {})
166
+ uri = generate_uri(container)
167
+ uri.query = URI.encode_www_form(restype: "container")
168
+ response = Http.new(uri, signer:, raise_on_error: false).head
169
+
170
+ Container.new(response)
171
+ end
172
+
173
+ # Create the container
174
+ #
175
+ # Calls to {Create Container}[https://learn.microsoft.com/en-us/rest/api/storageservices/create-container]
176
+ def create_container(options = {})
177
+ uri = generate_uri(container)
178
+ uri.query = URI.encode_www_form(restype: "container")
179
+ response = Http.new(uri, signer:).put
180
+ end
181
+
182
+ # Delete the container
183
+ #
184
+ # Calls to {Delete Container}[https://learn.microsoft.com/en-us/rest/api/storageservices/delete-container]
185
+ def delete_container(options = {})
186
+ uri = generate_uri(container)
187
+ uri.query = URI.encode_www_form(restype: "container")
188
+ response = Http.new(uri, signer:).delete
189
+ end
190
+
150
191
  # Return a URI object to a resource in the container. Takes a path.
151
192
  #
152
193
  # Example: +generate_uri("#{container}/#{key}")+
@@ -299,10 +340,10 @@ module AzureBlob
299
340
  Http.new(uri, headers, metadata: options[:metadata], signer:).put(content.read)
300
341
  end
301
342
 
302
- attr_reader :account_name, :signer, :container, :http
303
-
304
343
  def host
305
- "https://#{account_name}.blob.core.windows.net"
344
+ @host ||= "https://#{account_name}.blob.#{CLOUD_REGIONS_SUFFIX[cloud_regions]}"
306
345
  end
346
+
347
+ attr_reader :account_name, :signer, :container, :http, :cloud_regions
307
348
  end
308
349
  end
@@ -2,7 +2,13 @@
2
2
 
3
3
  module AzureBlob
4
4
  API_VERSION = "2024-05-04"
5
- MAX_UPLOAD_SIZE = 256 * 1024 * 1024
6
- DEFAULT_BLOCK_SIZE = 128 * 1024 * 1024
5
+ MAX_UPLOAD_SIZE = 256 * 1024 * 1024 # 256 Megabytes
6
+ DEFAULT_BLOCK_SIZE = 128 * 1024 * 1024 # 128 Megabytes
7
7
  BLOB_SERVICE = "b"
8
+ CLOUD_REGIONS_SUFFIX = {
9
+ global: "core.windows.net",
10
+ cn: "core.chinacloudapi.cn",
11
+ de: "core.cloudapi.de",
12
+ usgovt: "core.usgovcloudapi.net",
13
+ }
8
14
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AzureBlob
4
+ # AzureBlob::Container holds the metadata for a given Container.
5
+ class Container
6
+ # You should not instanciate this object directly,
7
+ # but obtain one when calling relevant methods of AzureBlob::Client.
8
+ #
9
+ # Expects a Net::HTTPResponse object from a
10
+ # HEAD or GET request to a container uri.
11
+ def initialize(response)
12
+ @response = response
13
+ end
14
+
15
+
16
+ def present?
17
+ response.code == "200"
18
+ end
19
+
20
+ # Returns the custom Azure metadata tagged on the container.
21
+ def metadata
22
+ @metadata || response
23
+ .to_hash
24
+ .select { |key, _| key.start_with?("x-ms-meta") }
25
+ .transform_values(&:first)
26
+ .transform_keys { |key| key.delete_prefix("x-ms-meta-").to_sym }
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :response
32
+ end
33
+ end
@@ -12,10 +12,12 @@ module AzureBlob
12
12
  class EntraIdSigner # :nodoc:
13
13
  attr_reader :token
14
14
  attr_reader :account_name
15
+ attr_reader :host
15
16
 
16
- def initialize(account_name:, principal_id: nil)
17
+ def initialize(account_name:, host:, principal_id: nil)
17
18
  @token = AzureBlob::IdentityToken.new(principal_id:)
18
19
  @account_name = account_name
20
+ @host = host
19
21
  end
20
22
 
21
23
  def authorization_header(uri:, verb:, headers: {})
@@ -12,7 +12,10 @@ module AzureBlob
12
12
  def initialize(body: nil, status: nil)
13
13
  @body = body
14
14
  @status = status
15
- super(body)
15
+ end
16
+
17
+ def inspect
18
+ @body
16
19
  end
17
20
  end
18
21
  class FileNotFoundError < Error; end
@@ -21,7 +24,8 @@ module AzureBlob
21
24
 
22
25
  include REXML
23
26
 
24
- def initialize(uri, headers = {}, signer: nil, metadata: {}, debug: false)
27
+ def initialize(uri, headers = {}, signer: nil, metadata: {}, debug: false, raise_on_error: true)
28
+ @raise_on_error = raise_on_error
25
29
  @date = Time.now.httpdate
26
30
  @uri = uri
27
31
  @signer = signer
@@ -42,7 +46,7 @@ module AzureBlob
42
46
  response.body
43
47
  end
44
48
 
45
- def put(content)
49
+ def put(content = "")
46
50
  sign_request("PUT") if signer
47
51
  @response = http.start do |http|
48
52
  http.put(uri, content, headers)
@@ -51,7 +55,7 @@ module AzureBlob
51
55
  true
52
56
  end
53
57
 
54
- def post(content)
58
+ def post(content = "")
55
59
  sign_request("POST") if signer
56
60
  @response = http.start do |http|
57
61
  http.post(uri, content, headers)
@@ -107,6 +111,7 @@ module AzureBlob
107
111
  end
108
112
 
109
113
  def raise_error
114
+ return unless raise_on_error
110
115
  raise error_from_response.new(body: @response.body, status: @response.code&.to_i)
111
116
  end
112
117
 
@@ -115,13 +120,13 @@ module AzureBlob
115
120
  end
116
121
 
117
122
  def azure_error_code
118
- Document.new(response.body).get_elements("//Error/Code").first.get_text.to_s
123
+ Document.new(response.body).get_elements("//Error/Code").first.get_text.to_s if response.body
119
124
  end
120
125
 
121
126
  def error_from_response
122
127
  ERROR_MAPPINGS[status] || ERROR_CODE_MAPPINGS[azure_error_code] || Error
123
128
  end
124
129
 
125
- attr_accessor :host, :http, :signer, :response, :headers, :uri, :date
130
+ attr_accessor :host, :http, :signer, :response, :headers, :uri, :date, :raise_on_error
126
131
  end
127
132
  end
@@ -6,7 +6,7 @@ module AzureBlob
6
6
  EXPIRATION_BUFFER = 3600 # 1 hours
7
7
  def initialize(account_name:, signer:)
8
8
  @uri = URI.parse(
9
- "https://#{account_name}.blob.core.windows.net/?restype=service&comp=userdelegationkey"
9
+ "#{signer.host}/?restype=service&comp=userdelegationkey"
10
10
  )
11
11
 
12
12
  @signer = signer
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AzureBlob
4
- VERSION = "0.5.0"
4
+ VERSION = "0.5.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: azure-blob
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joé Dupuis
@@ -31,20 +31,10 @@ executables: []
31
31
  extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
- - ".envrc"
35
- - ".rubocop.yml"
36
- - ".standard.yml"
37
- - ".terraform.lock.hcl"
38
34
  - CHANGELOG.md
39
35
  - LICENSE.txt
40
36
  - README.md
41
37
  - Rakefile
42
- - azure-blob.gemspec
43
- - devenv.local.nix.example
44
- - devenv.lock
45
- - devenv.nix
46
- - devenv.yaml
47
- - input.tf
48
38
  - lib/active_storage/service/azure_blob_service.rb
49
39
  - lib/azure_blob.rb
50
40
  - lib/azure_blob/blob.rb
@@ -54,6 +44,7 @@ files:
54
44
  - lib/azure_blob/canonicalized_resource.rb
55
45
  - lib/azure_blob/client.rb
56
46
  - lib/azure_blob/const.rb
47
+ - lib/azure_blob/container.rb
57
48
  - lib/azure_blob/entra_id_signer.rb
58
49
  - lib/azure_blob/errors.rb
59
50
  - lib/azure_blob/http.rb
@@ -62,8 +53,6 @@ files:
62
53
  - lib/azure_blob/shared_key_signer.rb
63
54
  - lib/azure_blob/user_delegation_key.rb
64
55
  - lib/azure_blob/version.rb
65
- - main.tf
66
- - output.tf
67
56
  homepage: https://github.com/testdouble/azure-blob
68
57
  licenses:
69
58
  - MIT
@@ -90,5 +79,5 @@ requirements: []
90
79
  rubygems_version: 3.3.27
91
80
  signing_key:
92
81
  specification_version: 4
93
- summary: Azure blob client
82
+ summary: Azure Blob client and Active Storage adapter
94
83
  test_files: []
data/.envrc DELETED
@@ -1,3 +0,0 @@
1
- source_url "https://raw.githubusercontent.com/cachix/devenv/d1f7b48e35e6dee421cfd0f51481d17f77586997/direnvrc" "sha256-YBzqskFZxmNb3kYVoKD9ZixoPXJh1C9ZvTLGFRkauZ0="
2
-
3
- use devenv
data/.rubocop.yml DELETED
@@ -1,25 +0,0 @@
1
- AllCops:
2
- TargetRubyVersion: 3.1
3
-
4
- # Omakase Ruby styling for Rails
5
- inherit_gem: { rubocop-rails-omakase: rubocop.yml }
6
-
7
- # Overwrite or add rules to create your own house style
8
- #
9
- # # Use `[a, [b, c]]` not `[ a, [ b, c ] ]`
10
- # Layout/SpaceInsideArrayLiteralBrackets:
11
- # Enabled: false
12
-
13
- Style/TrailingCommaInArrayLiteral:
14
- Enabled: true
15
- EnforcedStyleForMultiline: consistent_comma
16
-
17
- Style/TrailingCommaInHashLiteral:
18
- Enabled: true
19
- EnforcedStyleForMultiline: consistent_comma
20
-
21
-
22
- Rails/AssertNot:
23
- Enabled: false
24
- Rails/RefuteMethods:
25
- Enabled: false
data/.standard.yml DELETED
@@ -1,3 +0,0 @@
1
- # For available configuration options, see:
2
- # https://github.com/standardrb/standard
3
- ruby_version: 3.1
data/.terraform.lock.hcl DELETED
@@ -1,22 +0,0 @@
1
- # This file is maintained automatically by "terraform init".
2
- # Manual edits may be lost in future updates.
3
-
4
- provider "registry.terraform.io/hashicorp/azurerm" {
5
- version = "3.113.0"
6
- constraints = "~> 3.0"
7
- hashes = [
8
- "h1:eEUtt0lrLdpVaF6FiDq8BGQPgEcykmhj0aNIL7hTOGw=",
9
- "zh:12479f5664288943400447b55e50df675c28ae82ad8d373cc2e5682f3a3411f0",
10
- "zh:1b42a14e80e568429d3b55fed753ca3ef0df9dcdfa107890d7264599c020940f",
11
- "zh:381be6ca617f848de3baa3985a6e1788e91a803afe04a3c5c727453528b6310d",
12
- "zh:3e70e2e07b6db1c363de3e5d0ca47f27fc956473df03329c7d2e54d3ac29176b",
13
- "zh:87c7633aeaa828098c6055da9e67d4acaf4b46748b6b3f0267e105e55f05de25",
14
- "zh:8d0d98226901f874770dd5220d4701a12ae8bd586994615aa7dcba12b9736bec",
15
- "zh:9fd913acd42a60c3a90a18ce803567ef861db8779a59aacced91f2cbd86de9d9",
16
- "zh:b6f3f7ae0a055437fb36c139af9bb3135e7f4dad172157ae1eb0177dc74d703f",
17
- "zh:b927027ba2bf40d34e03d742fd2b6c5299023b5ab8e6f05e50aac76a46ad1094",
18
- "zh:ceb5187b9d2a439f4e48944f3ffeeeaf47a03dbe6f3325ea1775bf659ce0aa88",
19
- "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
20
- "zh:fb9d78dfeca7489bffca9b1a1f3abee7f16dbbcba31388aea1102062c1d6dce8",
21
- ]
22
- }
data/azure-blob.gemspec DELETED
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "lib/azure_blob/version"
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "azure-blob"
7
- spec.version = AzureBlob::VERSION
8
- spec.authors = [ "Joé Dupuis" ]
9
- spec.email = [ "joe@dupuis.io" ]
10
-
11
- spec.summary = "Azure blob client"
12
- spec.homepage = "https://github.com/testdouble/azure-blob"
13
- spec.license = "MIT"
14
- spec.required_ruby_version = ">= 3.1"
15
-
16
- spec.metadata["rubygems_mfa_required"] = "true"
17
- spec.metadata["homepage_uri"] = spec.homepage
18
- spec.metadata["source_code_uri"] = spec.homepage
19
- spec.metadata["changelog_uri"] = "https://github.com/testdouble/azure-blob/blob/main/CHANGELOG.md"
20
-
21
- spec.add_dependency "rexml"
22
-
23
- # Specify which files should be added to the gem when it is released.
24
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
- spec.files = Dir.chdir(__dir__) do
26
- `git ls-files -z`.split("\x0").reject do |f|
27
- (File.expand_path(f) == __FILE__) ||
28
- f.start_with?(*%w[bin/ test/ spec/ features/ .git .github appveyor Gemfile])
29
- end
30
- end
31
- spec.bindir = "exe"
32
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
33
- spec.require_paths = [ "lib" ]
34
- end
@@ -1,8 +0,0 @@
1
- {pkgs, lib, ...}:{
2
- env = {
3
- AZURE_ACCOUNT_NAME = "";
4
- AZURE_ACCESS_KEY = "";
5
- AZURE_PRIVATE_CONTAINER = "";
6
- AZURE_PUBLIC_CONTAINER = "";
7
- };
8
- }
data/devenv.lock DELETED
@@ -1,242 +0,0 @@
1
- {
2
- "nodes": {
3
- "devenv": {
4
- "locked": {
5
- "dir": "src/modules",
6
- "lastModified": 1715593316,
7
- "narHash": "sha256-S7XatU9uV3q9bVBcg/ER0VMQcnPZprrVlN209ne7LDw=",
8
- "owner": "cachix",
9
- "repo": "devenv",
10
- "rev": "725c90407ef53cc2a1b53701c6d2d0745cf2484f",
11
- "type": "github"
12
- },
13
- "original": {
14
- "dir": "src/modules",
15
- "owner": "cachix",
16
- "repo": "devenv",
17
- "type": "github"
18
- }
19
- },
20
- "flake-compat": {
21
- "flake": false,
22
- "locked": {
23
- "lastModified": 1696426674,
24
- "owner": "edolstra",
25
- "repo": "flake-compat",
26
- "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
27
- "treeHash": "2addb7b71a20a25ea74feeaf5c2f6a6b30898ecb",
28
- "type": "github"
29
- },
30
- "original": {
31
- "owner": "edolstra",
32
- "repo": "flake-compat",
33
- "type": "github"
34
- }
35
- },
36
- "flake-compat_2": {
37
- "flake": false,
38
- "locked": {
39
- "lastModified": 1696426674,
40
- "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
41
- "owner": "edolstra",
42
- "repo": "flake-compat",
43
- "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
44
- "type": "github"
45
- },
46
- "original": {
47
- "owner": "edolstra",
48
- "repo": "flake-compat",
49
- "type": "github"
50
- }
51
- },
52
- "flake-utils": {
53
- "inputs": {
54
- "systems": "systems"
55
- },
56
- "locked": {
57
- "lastModified": 1710146030,
58
- "owner": "numtide",
59
- "repo": "flake-utils",
60
- "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
61
- "treeHash": "bd263f021e345cb4a39d80c126ab650bebc3c10c",
62
- "type": "github"
63
- },
64
- "original": {
65
- "owner": "numtide",
66
- "repo": "flake-utils",
67
- "type": "github"
68
- }
69
- },
70
- "flake-utils_2": {
71
- "inputs": {
72
- "systems": "systems_2"
73
- },
74
- "locked": {
75
- "lastModified": 1710146030,
76
- "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
77
- "owner": "numtide",
78
- "repo": "flake-utils",
79
- "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
80
- "type": "github"
81
- },
82
- "original": {
83
- "owner": "numtide",
84
- "repo": "flake-utils",
85
- "type": "github"
86
- }
87
- },
88
- "gitignore": {
89
- "inputs": {
90
- "nixpkgs": [
91
- "pre-commit-hooks",
92
- "nixpkgs"
93
- ]
94
- },
95
- "locked": {
96
- "lastModified": 1709087332,
97
- "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
98
- "owner": "hercules-ci",
99
- "repo": "gitignore.nix",
100
- "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
101
- "type": "github"
102
- },
103
- "original": {
104
- "owner": "hercules-ci",
105
- "repo": "gitignore.nix",
106
- "type": "github"
107
- }
108
- },
109
- "nixpkgs": {
110
- "locked": {
111
- "lastModified": 1725001927,
112
- "owner": "NixOS",
113
- "repo": "nixpkgs",
114
- "rev": "6e99f2a27d600612004fbd2c3282d614bfee6421",
115
- "treeHash": "1e85443cc9f0ba302df2cf61cacb8014943e2d19",
116
- "type": "github"
117
- },
118
- "original": {
119
- "owner": "NixOS",
120
- "ref": "nixos-24.05",
121
- "repo": "nixpkgs",
122
- "type": "github"
123
- }
124
- },
125
- "nixpkgs-ruby": {
126
- "inputs": {
127
- "flake-compat": "flake-compat",
128
- "flake-utils": "flake-utils",
129
- "nixpkgs": "nixpkgs_2"
130
- },
131
- "locked": {
132
- "lastModified": 1724737223,
133
- "owner": "bobvanderlinden",
134
- "repo": "nixpkgs-ruby",
135
- "rev": "175b5867babcbc471b94be9fd5576f2973bbdb6d",
136
- "treeHash": "2fe3404ac0eeb7bcb7ac7b5f5f8b9b6a7e460147",
137
- "type": "github"
138
- },
139
- "original": {
140
- "owner": "bobvanderlinden",
141
- "repo": "nixpkgs-ruby",
142
- "type": "github"
143
- }
144
- },
145
- "nixpkgs-stable": {
146
- "locked": {
147
- "lastModified": 1710695816,
148
- "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=",
149
- "owner": "NixOS",
150
- "repo": "nixpkgs",
151
- "rev": "614b4613980a522ba49f0d194531beddbb7220d3",
152
- "type": "github"
153
- },
154
- "original": {
155
- "owner": "NixOS",
156
- "ref": "nixos-23.11",
157
- "repo": "nixpkgs",
158
- "type": "github"
159
- }
160
- },
161
- "nixpkgs_2": {
162
- "locked": {
163
- "lastModified": 1725001927,
164
- "owner": "NixOS",
165
- "repo": "nixpkgs",
166
- "rev": "6e99f2a27d600612004fbd2c3282d614bfee6421",
167
- "treeHash": "1e85443cc9f0ba302df2cf61cacb8014943e2d19",
168
- "type": "github"
169
- },
170
- "original": {
171
- "owner": "NixOS",
172
- "ref": "nixos-24.05",
173
- "repo": "nixpkgs",
174
- "type": "github"
175
- }
176
- },
177
- "pre-commit-hooks": {
178
- "inputs": {
179
- "flake-compat": "flake-compat_2",
180
- "flake-utils": "flake-utils_2",
181
- "gitignore": "gitignore",
182
- "nixpkgs": [
183
- "nixpkgs"
184
- ],
185
- "nixpkgs-stable": "nixpkgs-stable"
186
- },
187
- "locked": {
188
- "lastModified": 1715609711,
189
- "narHash": "sha256-/5u29K0c+4jyQ8x7dUIEUWlz2BoTSZWUP2quPwFCE7M=",
190
- "owner": "cachix",
191
- "repo": "pre-commit-hooks.nix",
192
- "rev": "c182c876690380f8d3b9557c4609472ebfa1b141",
193
- "type": "github"
194
- },
195
- "original": {
196
- "owner": "cachix",
197
- "repo": "pre-commit-hooks.nix",
198
- "type": "github"
199
- }
200
- },
201
- "root": {
202
- "inputs": {
203
- "devenv": "devenv",
204
- "nixpkgs": "nixpkgs",
205
- "nixpkgs-ruby": "nixpkgs-ruby",
206
- "pre-commit-hooks": "pre-commit-hooks"
207
- }
208
- },
209
- "systems": {
210
- "locked": {
211
- "lastModified": 1681028828,
212
- "owner": "nix-systems",
213
- "repo": "default",
214
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
215
- "treeHash": "cce81f2a0f0743b2eb61bc2eb6c7adbe2f2c6beb",
216
- "type": "github"
217
- },
218
- "original": {
219
- "owner": "nix-systems",
220
- "repo": "default",
221
- "type": "github"
222
- }
223
- },
224
- "systems_2": {
225
- "locked": {
226
- "lastModified": 1681028828,
227
- "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
228
- "owner": "nix-systems",
229
- "repo": "default",
230
- "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
231
- "type": "github"
232
- },
233
- "original": {
234
- "owner": "nix-systems",
235
- "repo": "default",
236
- "type": "github"
237
- }
238
- }
239
- },
240
- "root": "root",
241
- "version": 7
242
- }
data/devenv.nix DELETED
@@ -1,36 +0,0 @@
1
- { pkgs, config, ... }:
2
-
3
- {
4
- env = {
5
- LD_LIBRARY_PATH = "${config.devenv.profile}/lib";
6
- };
7
-
8
- packages = with pkgs; [
9
- git
10
- libyaml
11
- terraform
12
- azure-cli
13
- glib
14
- vips
15
- sshuttle
16
- sshpass
17
- rsync
18
- ];
19
-
20
- languages.ruby.enable = true;
21
- languages.ruby.version = "3.1.6";
22
-
23
- scripts.generate-env-file.exec = ''
24
- terraform output -raw devenv_local_nix > devenv.local.nix
25
- '';
26
-
27
- scripts.proxy-vps.exec = ''
28
- exec sshuttle -e "ssh -o CheckHostIP=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" -r "$(terraform output --raw vm_username)@$(terraform output --raw vm_ip)" 0/0
29
- '';
30
-
31
- scripts.start-app-service-ssh.exec = ''
32
- resource_group=$(terraform output --raw "resource_group")
33
- app_name=$(terraform output --raw "app_service_app_name")
34
- exec az webapp create-remote-connection --resource-group $resource_group --name $app_name
35
- '';
36
- }
data/devenv.yaml DELETED
@@ -1,6 +0,0 @@
1
- allowUnfree: true
2
- inputs:
3
- nixpkgs:
4
- url: github:NixOS/nixpkgs/nixos-24.05
5
- nixpkgs-ruby:
6
- url: github:bobvanderlinden/nixpkgs-ruby
data/input.tf DELETED
@@ -1,44 +0,0 @@
1
- variable "location" {
2
- type = string
3
- default = "westus2"
4
- }
5
-
6
- variable "prefix" {
7
- type = string
8
- default = "azure-blob"
9
- }
10
-
11
- variable "storage_account_name" {
12
- type = string
13
- default = "azureblobrubygemdev"
14
- }
15
-
16
- variable "create_vm" {
17
- type = bool
18
- default = false
19
- }
20
-
21
- variable "vm_size" {
22
- type = string
23
- default = "Standard_B2s"
24
- }
25
-
26
- variable "vm_username" {
27
- type = string
28
- default = "azureblob"
29
- }
30
-
31
- variable "vm_password" {
32
- type = string
33
- default = "qwe123QWE!@#"
34
- }
35
-
36
- variable "create_app_service" {
37
- type = bool
38
- default = false
39
- }
40
-
41
- variable "ssh_key" {
42
- type = string
43
- default = ""
44
- }
data/main.tf DELETED
@@ -1,187 +0,0 @@
1
- terraform {
2
- required_providers {
3
- azurerm = {
4
- source = "hashicorp/azurerm"
5
- version = "~>3.0"
6
- }
7
- }
8
- }
9
-
10
- provider "azurerm" {
11
- features {}
12
- }
13
-
14
- locals {
15
- public_ssh_key = var.ssh_key != "" ? var.ssh_key : file("~/.ssh/id_rsa.pub")
16
- }
17
-
18
- resource "azurerm_resource_group" "main" {
19
- name = var.prefix
20
- location = var.location
21
- tags = {
22
- source = "Terraform"
23
- }
24
- }
25
-
26
- resource "azurerm_storage_account" "main" {
27
- name = var.storage_account_name
28
- resource_group_name = azurerm_resource_group.main.name
29
- location = azurerm_resource_group.main.location
30
- account_tier = "Standard"
31
- account_replication_type = "LRS"
32
-
33
- tags = {
34
- source = "Terraform"
35
- }
36
- }
37
-
38
- resource "azurerm_storage_container" "private" {
39
- name = "private"
40
- storage_account_name = azurerm_storage_account.main.name
41
- container_access_type = "private"
42
- }
43
-
44
- resource "azurerm_storage_container" "public" {
45
- name = "public"
46
- storage_account_name = azurerm_storage_account.main.name
47
- container_access_type = "blob"
48
- }
49
-
50
- resource "azurerm_virtual_network" "main" {
51
- count = var.create_vm ? 1 : 0
52
- name = "${var.prefix}-network"
53
- address_space = ["10.0.0.0/16"]
54
- location = azurerm_resource_group.main.location
55
- resource_group_name = azurerm_resource_group.main.name
56
-
57
- tags = {
58
- source = "Terraform"
59
- }
60
- }
61
-
62
- resource "azurerm_subnet" "main" {
63
- count = var.create_vm ? 1 : 0
64
- name = "${var.prefix}-main"
65
- resource_group_name = azurerm_resource_group.main.name
66
- virtual_network_name = azurerm_virtual_network.main[0].name
67
- address_prefixes = ["10.0.2.0/24"]
68
- }
69
-
70
- resource "azurerm_network_interface" "main" {
71
- count = var.create_vm ? 1 : 0
72
- name = "${var.prefix}-nic"
73
- location = azurerm_resource_group.main.location
74
- resource_group_name = azurerm_resource_group.main.name
75
-
76
- ip_configuration {
77
- name = "${var.prefix}-ip-config"
78
- subnet_id = azurerm_subnet.main[0].id
79
- private_ip_address_allocation = "Dynamic"
80
- public_ip_address_id = azurerm_public_ip.main[0].id
81
- }
82
-
83
- tags = {
84
- source = "Terraform"
85
- }
86
- }
87
-
88
- resource "azurerm_public_ip" "main" {
89
- count = var.create_vm ? 1 : 0
90
- name = "${var.prefix}-public-ip"
91
- resource_group_name = azurerm_resource_group.main.name
92
- location = azurerm_resource_group.main.location
93
- allocation_method = "Static"
94
-
95
- tags = {
96
- source = "Terraform"
97
- }
98
- }
99
-
100
- resource "azurerm_user_assigned_identity" "vm" {
101
- location = azurerm_resource_group.main.location
102
- name = "${var.prefix}-vm"
103
- resource_group_name = azurerm_resource_group.main.name
104
- }
105
-
106
-
107
- resource "azurerm_role_assignment" "vm" {
108
- scope = azurerm_storage_account.main.id
109
- role_definition_name = "Storage Blob Data Contributor"
110
- principal_id = azurerm_user_assigned_identity.vm.principal_id
111
- }
112
-
113
- resource "azurerm_linux_virtual_machine" "main" {
114
- count = var.create_vm ? 1 : 0
115
- name = "${var.prefix}-vm"
116
- computer_name = var.prefix
117
- resource_group_name = azurerm_resource_group.main.name
118
- location = azurerm_resource_group.main.location
119
- size = var.vm_size
120
- admin_username = var.vm_username
121
- admin_password = var.vm_password
122
- disable_password_authentication = true
123
- network_interface_ids = [azurerm_network_interface.main[0].id]
124
-
125
- identity {
126
- type = "UserAssigned"
127
- identity_ids = [azurerm_user_assigned_identity.vm.id]
128
- }
129
-
130
- admin_ssh_key {
131
- username = var.vm_username
132
- public_key = local.public_ssh_key
133
- }
134
-
135
- source_image_reference {
136
- publisher = "Canonical"
137
- offer = "0001-com-ubuntu-server-jammy"
138
- sku = "22_04-lts"
139
- version = "latest"
140
- }
141
-
142
- os_disk {
143
- caching = "ReadWrite"
144
- storage_account_type = "Standard_LRS"
145
- }
146
-
147
- tags = {
148
- source = "Terraform"
149
- }
150
- }
151
-
152
- resource "azurerm_service_plan" "main" {
153
- count = var.create_app_service ? 1 : 0
154
- name = "${var.prefix}-appserviceplan"
155
- resource_group_name = azurerm_resource_group.main.name
156
- location = azurerm_resource_group.main.location
157
- os_type = "Linux"
158
- sku_name = "B1"
159
- }
160
-
161
- resource "azurerm_linux_web_app" "main" {
162
- count = var.create_app_service ? 1 : 0
163
- name = "${var.prefix}-app"
164
- service_plan_id = azurerm_service_plan.main[0].id
165
- resource_group_name = azurerm_resource_group.main.name
166
- location = azurerm_resource_group.main.location
167
-
168
- identity {
169
- type = "UserAssigned"
170
- identity_ids = [azurerm_user_assigned_identity.vm.id]
171
- }
172
-
173
- site_config {
174
- application_stack {
175
- node_version = "20-lts"
176
- }
177
- }
178
- }
179
-
180
- resource "azurerm_app_service_source_control" "main" {
181
- count = var.create_app_service ? 1 : 0
182
- app_id = azurerm_linux_web_app.main[0].id
183
- repo_url = "https://github.com/Azure-Samples/nodejs-docs-hello-world"
184
- branch = "master"
185
- use_manual_integration = true
186
- use_mercurial = false
187
- }
data/output.tf DELETED
@@ -1,30 +0,0 @@
1
- output "devenv_local_nix" {
2
- sensitive = true
3
- value = <<EOT
4
- {pkgs, lib, ...}:{
5
- env = {
6
- AZURE_ACCOUNT_NAME = "${azurerm_storage_account.main.name}";
7
- AZURE_ACCESS_KEY = "${azurerm_storage_account.main.primary_access_key}";
8
- AZURE_PRIVATE_CONTAINER = "${azurerm_storage_container.private.name}";
9
- AZURE_PUBLIC_CONTAINER = "${azurerm_storage_container.public.name}";
10
- AZURE_PRINCIPAL_ID = "${azurerm_user_assigned_identity.vm.principal_id}";
11
- };
12
- }
13
- EOT
14
- }
15
-
16
- output "vm_ip" {
17
- value = var.create_vm ? azurerm_public_ip.main[0].ip_address : ""
18
- }
19
-
20
- output "vm_username" {
21
- value = var.vm_username
22
- }
23
-
24
- output "app_service_app_name" {
25
- value = var.create_app_service ? azurerm_linux_web_app.main[0].name : ""
26
- }
27
-
28
- output "resource_group" {
29
- value = azurerm_resource_group.main.name
30
- }