Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update all values of a DNS record #76

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CLOUDFLARE_EMAIL='your_cloudlare_email'
CLOUDFLARE_KEY='your_cloudflare_api_key'
CLOUDFLARE_TEST_ZONE_MANAGEMENT=true
35 changes: 19 additions & 16 deletions gems.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,26 @@
gemspec

group :maintenance, optional: true do
gem "bake-gem"
gem "bake-modernize"
gem "utopia-project"
gem "bake-gem"
gem "bake-modernize"

gem "utopia-project"
end

group :test do
gem "sus"
gem "covered"
gem "decode"
gem "rubocop"

gem "sus-fixtures-async"

gem "sinatra"
gem "webmock"

gem "bake-test"
gem "bake-test-external"
gem "sus"
gem "covered"
gem "decode"
gem "rubocop"

gem "sus-fixtures-async"

gem "sinatra"
gem "webmock"

gem "bake-test"
gem "bake-test-external"

gem "dotenv"
gem "pry"
end
13 changes: 12 additions & 1 deletion lib/cloudflare/dns.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,18 @@ def update_content(content, **options)
self
end
end


def update(type: nil, name: nil, content: nil, **options)
response = put(
type: type || @record[:type],
name: name || @record[:name],
content: content || @record[:content],
**options
)

@value = response.result
end

def type
result[:type]
end
Expand Down
40 changes: 23 additions & 17 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Cloudflare

It is a Ruby wrapper for the Cloudflare V4 API. It provides a light weight wrapper using `RestClient::Resource`. The wrapper functionality is limited to zones and DNS records at this time, *PRs welcome*.
It is a Ruby wrapper for the Cloudflare V4 API. It provides a light weight wrapper using `RestClient::Resource`. The wrapper functionality is limited to zones and DNS records at this time, _PRs welcome_.

[![Development Status](https://github.com/socketry/cloudflare/workflows/Test/badge.svg)](https://github.com/socketry/cloudflare/actions?workflow=Test)

## Installation

Add this line to your application's Gemfile:

``` ruby
```ruby
gem 'cloudflare'
```

Expand All @@ -24,7 +24,7 @@ Or install it yourself as:

Here are some basic examples. For more details, refer to the code and specs.

``` ruby
```ruby
require 'cloudflare'

# Grab some details from somewhere:
Expand All @@ -34,25 +34,30 @@ key = ENV['CLOUDFLARE_KEY']
Cloudflare.connect(key: key, email: email) do |connection|
# Get all available zones:
zones = connection.zones

# Get a specific zone:
zone = connection.zones.find_by_id("...")
zone = connection.zones.find_by_name("example.com")

# Get DNS records for a given zone:
dns_records = zone.dns_records

# Show some details of the DNS record:
dns_record = dns_records.first
puts dns_record.name

# Add a DNS record. Here we add an A record for `batman.example.com`:
zone = zones.find_by_name("example.com")
zone.dns_records.create('A', 'batman', '1.2.3.4', proxied: false)


# Update a DNS record. Here we update the A record above to be a CNAME record to 'nairobi.kanairo.com'
record = zone.dns_records.find_by_name("example.com") }
record.update(type: "CNAME", name: "nairobi", content: "kanairo.com", proxied: true)


# Get firewall rules:
all_rules = zone.firewall_rules

# Block an ip:
rule = zone.firewall_rules.set('block', '1.2.3.4', notes: "ssh dictionary attack")
end
Expand All @@ -62,7 +67,7 @@ end

You can read more about [bearer tokens here](https://blog.cloudflare.com/api-tokens-general-availability/). This allows you to limit priviledges.

``` ruby
```ruby
require 'cloudflare'

token = 'a_generated_api_token'
Expand All @@ -74,10 +79,10 @@ end

### Using with Async

``` ruby
```ruby
Async do
connection = Cloudflare.connect(...)

# ... do something with connection ...
ensure
connection.close
Expand All @@ -90,9 +95,10 @@ We welcome contributions to this project.

1. Fork it.
2. Create your feature branch (`git checkout -b my-new-feature`).
3. Commit your changes (`git commit -am 'Add some feature'`).
4. Push to the branch (`git push origin my-new-feature`).
5. Create new Pull Request.
3. Run `cp .env.example .env` to create a .env file and populate it with the required environment variables. Edit the new file appropriately
4. Commit your changes (`git commit -am 'Add some feature'`).
5. Push to the branch (`git push origin my-new-feature`).
6. Create new Pull Request.

### Developer Certificate of Origin

Expand All @@ -104,5 +110,5 @@ This project is best served by a collaborative and respectful environment. Treat

## See Also

- [Cloudflare::DNS::Update](https://github.com/ioquatix/cloudflare-dns-update) - A dynamic DNS updater based on this gem.
- [Rubyflare](https://github.com/trev/rubyflare) - Another implementation.
- [Cloudflare::DNS::Update](https://github.com/ioquatix/cloudflare-dns-update) - A dynamic DNS updater based on this gem.
- [Rubyflare](https://github.com/trev/rubyflare) - Another implementation.
73 changes: 73 additions & 0 deletions spec/cloudflare/dns_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@

require 'cloudflare/rspec/connection'

RSpec.describe Cloudflare::DNS, order: :defined, timeout: 30 do
include_context Cloudflare::Zone

let(:subdomain) {"www-#{job_id}"}

after do
if defined? @record
expect(@record.delete).to be_success
end
end

describe "#create" do
it "can create dns record" do
@record = zone.dns_records.create("A", subdomain, "1.2.3.4")
expect(@record.type).to be == "A"
expect(@record.name).to be_start_with subdomain
expect(@record.content).to be == "1.2.3.4"
end

it "can create dns record with proxied option" do
@record = zone.dns_records.create("A", subdomain, "1.2.3.4", proxied: true)
expect(@record.type).to be == "A"
expect(@record.name).to be_start_with subdomain
expect(@record.content).to be == "1.2.3.4"
expect(@record.proxied).to be_truthy
end
end

describe "#update_content" do
let(:record) {@record = zone.dns_records.create("A", subdomain, "1.2.3.4")}

it "can update dns content" do
record.update_content("4.3.2.1")
expect(record.content).to be == "4.3.2.1"

fetched_record = zone.dns_records.find_by_name(record.name)
expect(fetched_record.content).to be == record.content
end

it "can update dns content with proxied option" do
record.update_content("4.3.2.1", proxied: true)
expect(record.proxied).to be_truthy

fetched_record = zone.dns_records.find_by_name(record.name)
expect(fetched_record.proxied).to be_truthy
end
end

describe "#update" do
let(:subject) { record.update(**new_params)}

let(:record) { @record = zone.dns_records.create("A", "old", "1.2.3.4", proxied: false) }

let(:new_params) do
{
type: "CNAME",
name: "new",
content: "example.com",
proxied: true
}
end

it "can update dns record" do
expect { subject }.to change { record.name }.to("#{new_params[:name]}.#{zone.name}")
.and change { record.type }.to(new_params[:type])
.and change { record.content }.to(new_params[:content])
.and change { record.proxied }.to(new_params[:proxied])
end
end
end
61 changes: 61 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# frozen_string_literal: true

require 'dotenv/load'

AUTH_EMAIL = ENV['CLOUDFLARE_EMAIL']
AUTH_KEY = ENV['CLOUDFLARE_KEY']

if AUTH_EMAIL.nil? || AUTH_EMAIL.empty? || AUTH_KEY.nil? || AUTH_KEY.empty?
puts 'Please make sure you have defined CLOUDFLARE_EMAIL and CLOUDFLARE_KEY in your environment'
puts 'You can also specify CLOUDFLARE_ZONE_NAME to test with your own zone and'
puts 'CLOUDFLARE_ACCOUNT_ID to use a specific account'
exit(1)
end

ACCOUNT_ID = ENV['CLOUDFLARE_ACCOUNT_ID']
NAMES = %w{alligator ant bear bee bird camel cat cheetah chicken chimpanzee cow crocodile deer dog dolphin duck eagle elephant fish fly fox frog giraffe goat goldfish hamster hippopotamus horse kangaroo kitten lion lobster monkey octopus owl panda pig puppy rabbit rat scorpion seal shark sheep snail snake spider squirrel tiger turtle wolf zebra}
JOB_ID = ENV.fetch('INVOCATION_ID', 'testing').hash
ZONE_NAME = ENV['CLOUDFLARE_ZONE_NAME'] || "#{NAMES[JOB_ID % NAMES.size]}.com"

$stderr.puts "Using zone name: #{ZONE_NAME}"

require 'covered/rspec'
require 'async/rspec'
require 'pry'

require 'cloudflare/rspec/connection'
require 'cloudflare/zones'

Dir[File.expand_path('../support/**/*.rb', __FILE__)].each{|path| require path}

RSpec.configure do |config|
# Enable flags like --only-failures and --next-failure
config.example_status_persistence_file_path = '.rspec_status'

config.expect_with :rspec do |c|
c.syntax = :expect
end

disabled_specs = {}

# Check for features the current account has enabled
Cloudflare.connect(key: AUTH_KEY, email: AUTH_EMAIL) do |conn|
begin
account = if ACCOUNT_ID
conn.accounts.find_by_id(ACCOUNT_ID)
else
conn.accounts.first
end
account.kv_namespaces.to_a
rescue Cloudflare::RequestError => e
if e.message.include?('your account is not entitled')
puts 'Disabling KV specs due to no access'
disabled_specs[:kv_spec] = true
else
raise
end
end
end

config.filter_run_excluding disabled_specs unless disabled_specs.empty?
end
13 changes: 13 additions & 0 deletions spec/support/cloudflare/account.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

RSpec.shared_context Cloudflare::Account do
include_context Cloudflare::RSpec::Connection

let(:account) do
if ACCOUNT_ID
connection.accounts.find_by_id(ACCOUNT_ID)
else
connection.accounts.first
end
end
end
17 changes: 17 additions & 0 deletions spec/support/cloudflare/zone.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

RSpec.shared_context Cloudflare::Zone do
include_context Cloudflare::Account

let(:job_id) { JOB_ID }
let(:names) { NAMES.dup }
let(:name) { ZONE_NAME.dup }

let(:zones) { connection.zones }

let(:zone) { @zone = zones.find_by_name(name) || zones.create(name, account) }

after do
@zone.delete if defined? @zone
end
end
Loading
Loading