Skip to content

Conversation

@ysokolovsky
Copy link

@ysokolovsky ysokolovsky commented Aug 12, 2025

This PR adds a new provider for OVH, enabling users to enumerate DNS records from their OVH accounts via the OVH API.
Issue#2 - Add Support for OVH DNS provider

Changes

  • Implemented ovh provider with DNS record retrieval (A, AAAA, CNAME)
  • Added proper authentication using OVH API credentials (application_key, application_secret, consumer_key) and configurable endpoint (defaults to ovh-eu)
  • Registered the provider in the inventory factory and Providers service map
  • Updated PROVIDERS.md with configuration and references
  • Added dependency: github.com/ovh/go-ovh

Example provider-config.yaml:

- provider: ovh
  id: ovh-mock
  endpoint: ovh-ca
  application_key: $OVH_APP_KEY
  application_secret: $OVH_APP_SECRET
  consumer_key: $OVH_CONSUMER_KEY

Usage / Testing

  1. Create OVH API credentials:
  • Generate an application key/secret and a consumer key for your account
  • Select the appropriate endpoint (ovh-eu, ovh-ca, or ovh-us)
  1. Configure the provider as shown above (env vars supported).
  2. Run cloudlist targeting the provider and DNS service:
    cloudlist -pc ovh.yaml -p ovh

Summary by CodeRabbit

  • New Features

    • Added OVH provider for DNS discovery (A, AAAA, CNAME) with aggregated resources and support for configurable services.
  • Documentation

    • Added OVH provider section with setup instructions, configuration example, and reference links.
  • Chores

    • Updated dependencies: added OVH client library and upgraded INI parsing dependency.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 12, 2025

Walkthrough

Adds a new OVH provider implementing DNS resource collection, registers it in the inventory, updates module dependencies to include the OVH client, and documents provider configuration in PROVIDERS.md.

Changes

Cohort / File(s) Summary of Changes
Documentation: OVH provider
PROVIDERS.md
Adds OVH provider documentation: provider key ovh, service dns, required auth keys (application_key, application_secret, consumer_key), endpoints (default ovh-eu, common: ovh-eu, ovh-ca, ovh-us), example config and reference links.
Module dependencies
go.mod
Upgrades gopkg.in/ini.v1 to v1.67.0 and adds indirect dependency github.com/ovh/go-ovh v1.9.0.
Inventory registration
pkg/inventory/inventory.go
Imports OVH provider package, registers "ovh" in Providers map, and handles "ovh" in the provider factory to call ovh.New(...).
OVH provider core
pkg/providers/ovh/ovh.go
Adds Provider type and exported API: New(options), Name(), ID(), Services(), Resources(ctx). Validates credentials and endpoint, constructs OVH client and shared HTTP client, delegates to service-specific providers when collecting resources.
OVH DNS implementation
pkg/providers/ovh/dns.go
Implements dnsProvider and GetResource(ctx) to list zones and records (A, AAAA, CNAME) via OVH API, build DNSName and IP resources (wiring IPv4/IPv6 for A/AAAA), and aggregate resources for return.

Sequence Diagram(s)

sequenceDiagram
  participant Inv as Inventory
  participant OVH as ovh.Provider
  participant DNS as dnsProvider
  participant API as OVH API

  Inv->>OVH: New(options)
  OVH->>API: ovh.NewClient(endpoint, appKey, appSecret, consumerKey)
  Inv->>OVH: Resources(ctx)
  alt dns service enabled
    OVH->>DNS: instantiate with shared client
    DNS->>API: GET /domain/zone
    loop for each zone
      DNS->>API: GET /domain/zone/{zone}/record?fieldType=A|AAAA|CNAME
      loop for each record ID
        DNS->>API: GET /domain/zone/{zone}/record/{id}
        DNS-->>OVH: append DNSName / IP resources
      end
    end
  end
  OVH-->>Inv: Aggregated Resources
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • dogancanbakir

Poem

I nibble through zones by moonlit code,
Keys in paw, I trace each node.
A, AAAA, CNAME I bravely chase—
I stitch the records, map each space.
OVH fields bloomed in my log-sprint pace. 🐇✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 729dc95 and 86a3a6d.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (1)
  • go.mod (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • go.mod
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
PROVIDERS.md (1)

485-513: Fix bare URLs in the references section.

The static analysis tools correctly identified bare URLs that should be wrapped in angle brackets for proper Markdown formatting.

Apply this diff to fix the markdown formatting:

 References - 
-1. https://eu.api.ovh.com/console/?section=%2Fdomain&branch=v1
-2. https://api.ovh.com/createToken/
-3. https://help.ovhcloud.com/csm/en-gb-api-getting-started-ovhcloud-api
+1. <https://eu.api.ovh.com/console/?section=%2Fdomain&branch=v1>
+2. <https://api.ovh.com/createToken/>
+3. <https://help.ovhcloud.com/csm/en-gb-api-getting-started-ovhcloud-api>
pkg/providers/ovh/dns.go (2)

41-43: Consider logging errors for debugging purposes.

When fetching record types fails, the error is silently ignored with continue. Consider logging these errors for better observability during debugging.

Consider adding error logging to help with debugging:

 			if err := d.client.GetWithContext(ctx, path, &ids); err != nil {
+				// Log error for debugging: unable to fetch record type %s for zone %s: %v
 				continue
 			}

46-48: Consider logging errors for debugging purposes.

When fetching individual records fails, the error is silently ignored with continue. Consider logging these errors for better observability during debugging.

Consider adding error logging to help with debugging:

 				if err := d.client.GetWithContext(ctx, fmt.Sprintf("/domain/zone/%s/record/%d", zone, id), &rec); err != nil {
+					// Log error for debugging: unable to fetch record %d for zone %s: %v
 					continue
 				}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a15e4f8 and 729dc95.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (5)
  • PROVIDERS.md (1 hunks)
  • go.mod (2 hunks)
  • pkg/inventory/inventory.go (3 hunks)
  • pkg/providers/ovh/dns.go (1 hunks)
  • pkg/providers/ovh/ovh.go (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
pkg/inventory/inventory.go (1)
pkg/providers/ovh/ovh.go (2)
  • Services (15-15)
  • New (24-78)
pkg/providers/ovh/ovh.go (2)
pkg/schema/schema.go (5)
  • ServiceMap (251-251)
  • OptionBlock (192-192)
  • ErrNoSuchKey (163-165)
  • Resources (39-42)
  • NewResources (45-50)
pkg/inventory/inventory.go (1)
  • New (37-52)
pkg/providers/ovh/dns.go (3)
pkg/schema/schema.go (3)
  • Resources (39-42)
  • NewResources (45-50)
  • Resource (141-160)
pkg/providers/ovh/ovh.go (1)
  • Provider (17-22)
pkg/schema/validate/validate.go (3)
  • DNSName (72-72)
  • PublicIPv4 (73-73)
  • PublicIPv6 (74-74)
🪛 markdownlint-cli2 (0.17.2)
PROVIDERS.md

510-510: Bare URL used

(MD034, no-bare-urls)


511-511: Bare URL used

(MD034, no-bare-urls)


512-512: Bare URL used

(MD034, no-bare-urls)

🔇 Additional comments (7)
go.mod (2)

168-168: LGTM!

The gopkg.in/ini.v1 dependency upgrade from v1.66.6 to v1.67.0 is a minor version update that should be backward compatible.


217-217: LGTM!

The addition of github.com/ovh/go-ovh v1.9.0 as an indirect dependency is appropriate for the new OVH provider implementation.

pkg/inventory/inventory.go (3)

24-24: LGTM!

The import of the OVH provider package is correctly placed and follows the existing import structure.


74-74: LGTM!

The OVH provider is correctly registered in the Providers map with the appropriate services mapping.


130-131: LGTM!

The OVH provider case is correctly added to the nameToProvider switch statement, following the established pattern.

pkg/providers/ovh/ovh.go (2)

24-78: Well-structured provider initialization with proper error handling.

The New function correctly validates all required credentials, handles optional configuration with sensible defaults, and properly initializes the OVH client with appropriate timeout settings.


84-95: Clean resource collection implementation.

The Resources method correctly handles service-specific resource collection and properly propagates errors from the DNS provider.

Comment on lines +67 to +82
// For A/AAAA also append IP resource; skip CNAME target to avoid duplicates
res := &schema.Resource{
Public: true,
Provider: providerName,
ID: d.id,
Service: d.name(),
}

if strings.ToUpper(rec.FieldType) == "A" {
res.PublicIPv4 = rec.Target
} else if strings.ToUpper(rec.FieldType) == "AAAA" {
res.PublicIPv6 = rec.Target
}

list.Append(res)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid appending empty IP resources for CNAME records.

The code currently appends an IP resource even for CNAME records, resulting in an empty resource with no IP addresses set. This creates unnecessary entries in the resource list.

Apply this diff to skip appending empty IP resources:

-				// For A/AAAA also append IP resource; skip CNAME target to avoid duplicates
-				res := &schema.Resource{
-					Public:   true,
-					Provider: providerName,
-					ID:       d.id,
-					Service:  d.name(),
-				}
-
-				if strings.ToUpper(rec.FieldType) == "A" {
-					res.PublicIPv4 = rec.Target
-				} else if strings.ToUpper(rec.FieldType) == "AAAA" {
-					res.PublicIPv6 = rec.Target
-				}
-
-				list.Append(res)
+				// For A/AAAA records, append IP resource
+				if strings.ToUpper(rec.FieldType) == "A" || strings.ToUpper(rec.FieldType) == "AAAA" {
+					res := &schema.Resource{
+						Public:   true,
+						Provider: providerName,
+						ID:       d.id,
+						Service:  d.name(),
+					}
+
+					if strings.ToUpper(rec.FieldType) == "A" {
+						res.PublicIPv4 = rec.Target
+					} else {
+						res.PublicIPv6 = rec.Target
+					}
+
+					list.Append(res)
+				}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// For A/AAAA also append IP resource; skip CNAME target to avoid duplicates
res := &schema.Resource{
Public: true,
Provider: providerName,
ID: d.id,
Service: d.name(),
}
if strings.ToUpper(rec.FieldType) == "A" {
res.PublicIPv4 = rec.Target
} else if strings.ToUpper(rec.FieldType) == "AAAA" {
res.PublicIPv6 = rec.Target
}
list.Append(res)
}
// For A/AAAA records, append IP resource
if strings.ToUpper(rec.FieldType) == "A" || strings.ToUpper(rec.FieldType) == "AAAA" {
res := &schema.Resource{
Public: true,
Provider: providerName,
ID: d.id,
Service: d.name(),
}
if strings.ToUpper(rec.FieldType) == "A" {
res.PublicIPv4 = rec.Target
} else {
res.PublicIPv6 = rec.Target
}
list.Append(res)
}
🤖 Prompt for AI Agents
In pkg/providers/ovh/dns.go around lines 67 to 82, the code constructs and
appends an IP resource for every record including CNAMEs, producing empty
resources; change the logic so you only create and append the resource when the
record is an A or AAAA (or when PublicIPv4/PublicIPv6 would be set). Concretely,
either move the res := &schema.Resource{...} creation inside the A/AAAA branches
or add a guard after setting PublicIPv4/PublicIPv6 that skips appending (e.g.,
continue or return) when neither IP field is populated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants