diff --git a/internal/dnsprovider/providers_additional.go b/internal/dnsprovider/providers_additional.go index d9d8cc83..03f6f3ef 100644 --- a/internal/dnsprovider/providers_additional.go +++ b/internal/dnsprovider/providers_additional.go @@ -303,12 +303,19 @@ type ovhProvider struct { now func() time.Time } +// ovhCreatedRecord is the part of the domain.zone.Record that OVHcloud +// returns from POST /domain/zone/{zone}/record. +type ovhCreatedRecord struct { + ID int64 `json:"id"` +} + func (provider *ovhProvider) Present(ctx context.Context, zone, name, value string) (func(context.Context) error, error) { - var recordID int64 + var created ovhCreatedRecord body := map[string]any{"fieldType": "TXT", "subDomain": relativeName(name, zone), "target": value, "ttl": 60} - if err := provider.request(ctx, http.MethodPost, "/domain/zone/"+url.PathEscape(zone)+"/record", body, &recordID); err != nil { + if err := provider.request(ctx, http.MethodPost, "/domain/zone/"+url.PathEscape(zone)+"/record", body, &created); err != nil { return nil, err } + recordID := created.ID if recordID == 0 { return nil, errors.New("OVHcloud did not return the created record ID") } diff --git a/internal/dnsprovider/providers_test.go b/internal/dnsprovider/providers_test.go index 8f87ec06..8bef2e3e 100644 --- a/internal/dnsprovider/providers_test.go +++ b/internal/dnsprovider/providers_test.go @@ -235,7 +235,7 @@ func TestOVHProviderRefreshesAfterCreateAndCleanup(t *testing.T) { t.Errorf("signature = %q", request.Header.Get("X-Ovh-Signature")) } if strings.HasSuffix(request.URL.Path, "/record") { - return httpResponse(http.StatusOK, `42`), nil + return httpResponse(http.StatusOK, `{"id":42,"zone":"example.com","fieldType":"TXT","subDomain":"_acme-challenge","target":"challenge","ttl":60}`), nil } return httpResponse(http.StatusOK, `{}`), nil })} @@ -454,6 +454,38 @@ func TestHetznerEnsureRecordCreatesMissingRRSet(t *testing.T) { } } +func TestOVHEnsureRecordCreatesMissingRecord(t *testing.T) { + var requests []string + client := &http.Client{Transport: roundTripFunc(func(request *http.Request) (*http.Response, error) { + requests = append(requests, request.Method+" "+request.URL.Path) + switch { + case request.Method == http.MethodGet: + return httpResponse(http.StatusOK, `[]`), nil + case strings.HasSuffix(request.URL.Path, "/record"): + return httpResponse(http.StatusOK, `{"id":42,"zone":"example.com","fieldType":"A","subDomain":"home","target":"198.51.100.1","ttl":600}`), nil + } + return httpResponse(http.StatusOK, `null`), nil + })} + provider := &ovhProvider{ + credentials: Credentials{ApplicationKey: "application", ApplicationSecret: "secret", ConsumerKey: "consumer"}, + client: client, baseURL: "https://ovh.test/1.0", now: func() time.Time { return time.Unix(1_786_620_000, 0) }, + } + changed, err := provider.EnsureRecord(context.Background(), Record{ + Zone: "example.com", Name: "home.example.com", Type: TypeA, Value: "198.51.100.1", TTL: 600, + }) + if err != nil || !changed { + t.Fatalf("EnsureRecord() = %v, %v", changed, err) + } + expected := []string{ + "GET /1.0/domain/zone/example.com/record", + "POST /1.0/domain/zone/example.com/record", + "POST /1.0/domain/zone/example.com/refresh", + } + if strings.Join(requests, "\n") != strings.Join(expected, "\n") { + t.Fatalf("requests = %#v", requests) + } +} + func TestRecordTTLRangeUsesProviderLimits(t *testing.T) { if err := ValidateRecordTTL("godaddy", 300); err == nil { t.Fatal("GoDaddy accepted a TTL below its minimum") diff --git a/internal/dnsprovider/records.go b/internal/dnsprovider/records.go index 9b3a6dde..da63f484 100644 --- a/internal/dnsprovider/records.go +++ b/internal/dnsprovider/records.go @@ -678,7 +678,7 @@ func (provider *ovhProvider) EnsureRecord(ctx context.Context, record Record) (b } body := map[string]any{"fieldType": record.Type, "subDomain": relative, "target": record.Value, "ttl": record.TTL} if len(ids) == 0 { - var created int64 + var created ovhCreatedRecord if err := provider.request(ctx, http.MethodPost, basePath, body, &created); err != nil { return false, err }