diff --git a/cloudmock/scaleway/mockdns/api.go b/cloudmock/scaleway/mockdns/api.go new file mode 100644 index 0000000000..806be1144e --- /dev/null +++ b/cloudmock/scaleway/mockdns/api.go @@ -0,0 +1,124 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mockdns + +import ( + "fmt" + + "github.com/google/uuid" + domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +type FakeDomainAPI struct { + DNSZones []*domain.DNSZone + Records map[string]*domain.Record +} + +func (f *FakeDomainAPI) ListDNSZones(req *domain.ListDNSZonesRequest, opts ...scw.RequestOption) (*domain.ListDNSZonesResponse, error) { + if f.DNSZones == nil { + return nil, fmt.Errorf("error response") + } + return &domain.ListDNSZonesResponse{ + TotalCount: uint32(len(f.DNSZones)), + DNSZones: f.DNSZones, + }, nil +} + +func (f *FakeDomainAPI) CreateDNSZone(req *domain.CreateDNSZoneRequest, opts ...scw.RequestOption) (*domain.DNSZone, error) { + if f.DNSZones == nil { + return nil, fmt.Errorf("error response") + } + newZone := &domain.DNSZone{ + Domain: req.Domain, + Subdomain: req.Subdomain, + } + f.DNSZones = append(f.DNSZones, newZone) + return newZone, nil +} + +func (f *FakeDomainAPI) DeleteDNSZone(req *domain.DeleteDNSZoneRequest, opts ...scw.RequestOption) (*domain.DeleteDNSZoneResponse, error) { + if f.DNSZones == nil { + return nil, fmt.Errorf("error response") + } + var newZoneList []*domain.DNSZone + for _, zone := range f.DNSZones { + if req.DNSZone == fmt.Sprintf("%s.%s", zone.Subdomain, zone.Domain) { + continue + } + newZoneList = append(newZoneList, zone) + } + f.DNSZones = newZoneList + return &domain.DeleteDNSZoneResponse{}, nil +} + +func (f *FakeDomainAPI) ListDNSZoneRecords(req *domain.ListDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.ListDNSZoneRecordsResponse, error) { + var recordsList []*domain.Record + for _, record := range f.Records { + recordsList = append(recordsList, record) + } + records := &domain.ListDNSZoneRecordsResponse{ + TotalCount: uint32(len(f.Records)), + Records: recordsList, + } + return records, nil +} + +func (f *FakeDomainAPI) UpdateDNSZoneRecords(req *domain.UpdateDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.UpdateDNSZoneRecordsResponse, error) { + for _, change := range req.Changes { + + if change.Add != nil { + for _, toAdd := range change.Add.Records { + toAdd.ID = uuid.New().String() + f.Records[toAdd.Name] = toAdd + } + + } else if change.Set != nil { + if len(change.Set.Records) != 1 { + fmt.Printf("only 1 record change will be applied from %d changes requested", len(change.Set.Records)) + } + for _, toUpsert := range change.Set.Records { + if _, ok := f.Records[toUpsert.Name]; !ok { + return nil, fmt.Errorf("could not find record %s to upsert", toUpsert.Name) + } + toUpsert.ID = *change.Set.ID + f.Records[toUpsert.Name] = toUpsert + } + } else if change.Delete != nil { + found := false + for name, record := range f.Records { + if record.ID == *change.Delete.ID { + delete(f.Records, name) + found = true + break + } + } + if found == false { + return nil, fmt.Errorf("could not find record %s to delete", *change.Delete.ID) + } + + } else { + return nil, fmt.Errorf("mock DNS not implemented for this method") + } + } + + var recordsList []*domain.Record + for _, record := range f.Records { + recordsList = append(recordsList, record) + } + return &domain.UpdateDNSZoneRecordsResponse{Records: recordsList}, nil +} diff --git a/dns-controller/cmd/dns-controller/main.go b/dns-controller/cmd/dns-controller/main.go index d38c2de86e..9cd6f8e30f 100644 --- a/dns-controller/cmd/dns-controller/main.go +++ b/dns-controller/cmd/dns-controller/main.go @@ -38,6 +38,7 @@ import ( _ "k8s.io/kops/dnsprovider/pkg/dnsprovider/providers/do" _ "k8s.io/kops/dnsprovider/pkg/dnsprovider/providers/google/clouddns" _ "k8s.io/kops/dnsprovider/pkg/dnsprovider/providers/openstack/designate" + _ "k8s.io/kops/dnsprovider/pkg/dnsprovider/providers/scaleway" "k8s.io/kops/pkg/wellknownports" "k8s.io/kops/protokube/pkg/gossip" gossipdns "k8s.io/kops/protokube/pkg/gossip/dns" @@ -67,7 +68,7 @@ func main() { flags.BoolVar(&watchIngress, "watch-ingress", true, "Configure hostnames found in ingress resources") flags.StringSliceVar(&gossipSeeds, "gossip-seed", gossipSeeds, "If set, will enable gossip zones and seed using the provided addresses") flags.StringSliceVarP(&zones, "zone", "z", []string{}, "Configure permitted zones and their mappings") - flags.StringVar(&dnsProviderID, "dns", "aws-route53", "DNS provider we should use (aws-route53, google-clouddns, digitalocean, gossip, openstack-designate)") + flags.StringVar(&dnsProviderID, "dns", "aws-route53", "DNS provider we should use (aws-route53, google-clouddns, digitalocean, gossip, openstack-designate, scaleway)") flag.StringVar(&gossipProtocol, "gossip-protocol", "mesh", "mesh/memberlist") flags.StringVar(&gossipListen, "gossip-listen", fmt.Sprintf("0.0.0.0:%d", wellknownports.DNSControllerGossipWeaveMesh), "The address on which to listen if gossip is enabled") flags.StringVar(&gossipSecret, "gossip-secret", gossipSecret, "Secret to use to secure gossip") diff --git a/dnsprovider/pkg/dnsprovider/providers/scaleway/dns.go b/dnsprovider/pkg/dnsprovider/providers/scaleway/dns.go new file mode 100644 index 0000000000..97e85b8b76 --- /dev/null +++ b/dnsprovider/pkg/dnsprovider/providers/scaleway/dns.go @@ -0,0 +1,458 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dns + +import ( + "context" + "fmt" + "io" + "os" + "strings" + + domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1" + "github.com/scaleway/scaleway-sdk-go/scw" + "golang.org/x/oauth2" + "k8s.io/klog/v2" + kopsv "k8s.io/kops" + "k8s.io/kops/dns-controller/pkg/dns" + "k8s.io/kops/dnsprovider/pkg/dnsprovider" + "k8s.io/kops/dnsprovider/pkg/dnsprovider/rrstype" +) + +var _ dnsprovider.Interface = Interface{} + +const ( + ProviderName = "scaleway" +) + +func init() { + dnsprovider.RegisterDNSProvider(ProviderName, func(config io.Reader) (dnsprovider.Interface, error) { + client, err := newClient() + if err != nil { + return nil, err + } + + return NewProvider(domain.NewAPI(client)), nil + }) +} + +// TokenSource implements oauth2.TokenSource +type TokenSource struct { + AccessToken string +} + +// Token returns oauth2.Token +func (t *TokenSource) Token() (*oauth2.Token, error) { + token := &oauth2.Token{ + AccessToken: t.AccessToken, + } + return token, nil +} + +func newClient() (*scw.Client, error) { + if accessKey := os.Getenv("SCW_ACCESS_KEY"); accessKey == "" { + return nil, fmt.Errorf("SCW_ACCESS_KEY is required") + } + if secretKey := os.Getenv("SCW_SECRET_KEY"); secretKey == "" { + return nil, fmt.Errorf("SCW_SECRET_KEY is required") + } + + scwClient, err := scw.NewClient( + scw.WithUserAgent("kubernetes-kops/"+kopsv.Version), + scw.WithEnv(), + ) + if err != nil { + return nil, err + } + + return scwClient, nil +} + +// Interface implements dnsprovider.Interface +type Interface struct { + domainAPI DomainAPI +} + +// NewProvider returns an implementation of dnsprovider.Interface +func NewProvider(api DomainAPI) dnsprovider.Interface { + return &Interface{domainAPI: api} +} + +// Zones returns an implementation of dnsprovider.Zones +func (d Interface) Zones() (dnsprovider.Zones, bool) { + return &zones{ + domainAPI: d.domainAPI, + }, true +} + +// zones is an implementation of dnsprovider.Zones +type zones struct { + domainAPI DomainAPI +} + +// List returns a list of all dns zones +func (z *zones) List() ([]dnsprovider.Zone, error) { + dnsZones, err := z.domainAPI.ListDNSZones(&domain.ListDNSZonesRequest{}, scw.WithAllPages()) + if err != nil { + return nil, fmt.Errorf("failed to list DNS zones: %w", err) + } + + zonesList := []dnsprovider.Zone(nil) + for _, dnsZone := range dnsZones.DNSZones { + newZone := &zone{ + name: dnsZone.Domain, + domainAPI: z.domainAPI, + } + zonesList = append(zonesList, newZone) + } + + return zonesList, nil +} + +// Add adds a new DNS zone. The name of the new zone should be of the form "name.domain", otherwise we can't infer the +// domain name from anywhere else in this function +func (z *zones) Add(newZone dnsprovider.Zone) (dnsprovider.Zone, error) { + newZoneNameSplit := strings.SplitN(newZone.Name(), ".", 2) + if len(newZoneNameSplit) < 2 { + return nil, fmt.Errorf("new zone name should contain at least 1 '.', got %q", newZone.Name()) + } + newZoneName := newZoneNameSplit[0] + domainName := newZoneNameSplit[1] + klog.V(8).Infof("Adding new DNS zone %s to domain %s", newZoneName, domainName) + + _, err := z.domainAPI.CreateDNSZone(&domain.CreateDNSZoneRequest{ + Subdomain: newZoneName, + Domain: domainName, + }) + if err != nil { + return nil, err + } + klog.V(4).Infof("Added new DNS zone %s to domain %s", newZoneName, domainName) + + return &zone{ + name: newZoneName, + domainAPI: z.domainAPI, + }, nil +} + +// Remove deletes a zone +func (z *zones) Remove(zone dnsprovider.Zone) error { + _, err := z.domainAPI.DeleteDNSZone(&domain.DeleteDNSZoneRequest{ + DNSZone: zone.Name(), + }) + if err != nil { + return err + } + + return nil +} + +// New returns a new implementation of dnsprovider.Zone +func (z *zones) New(name string) (dnsprovider.Zone, error) { + return &zone{ + name: name, + domainAPI: z.domainAPI, + }, nil +} + +// zone implements dnsprovider.Zone +type zone struct { + name string + domainAPI DomainAPI +} + +// Name returns the Name of a dns zone +func (z *zone) Name() string { + return z.name +} + +// ID returns the ID of a dns zone, here we use the name as an identifier +func (z *zone) ID() string { + return z.name +} + +// ResourceRecordSets returns an implementation of dnsprovider.ResourceRecordSets +func (z *zone) ResourceRecordSets() (dnsprovider.ResourceRecordSets, bool) { + return &resourceRecordSets{zone: z, domainAPI: z.domainAPI}, true +} + +// resourceRecordSets implements dnsprovider.ResourceRecordSet +type resourceRecordSets struct { + zone *zone + domainAPI DomainAPI +} + +// List returns a list of dnsprovider.ResourceRecordSet +func (r *resourceRecordSets) List() ([]dnsprovider.ResourceRecordSet, error) { + records, err := listRecords(r.domainAPI, r.zone.Name()) + if err != nil { + return nil, err + } + + var rrsets []dnsprovider.ResourceRecordSet + rrsetsWithoutDups := make(map[string]*resourceRecordSet) + + for _, record := range records { + // The scaleway API returns the record without the zone + // but the consumers of this interface expect the zone to be included + recordName := dns.EnsureDotSuffix(record.Name) + r.Zone().Name() + recordKey := recordName + "_" + record.Type.String() + if rrset, ok := rrsetsWithoutDups[recordKey]; !ok { + rrsetsWithoutDups[recordKey] = &resourceRecordSet{ + name: recordName, + data: []string{record.Data}, + ttl: int(record.TTL), + recordType: rrstype.RrsType(record.Type), + } + } else { + rrset.data = append(rrset.data, record.Data) + } + } + + for _, rrset := range rrsetsWithoutDups { + rrsets = append(rrsets, rrset) + } + + return rrsets, nil +} + +// Get returns a list of dnsprovider.ResourceRecordSet that matches the name parameter. The name should contain the domain name. +func (r *resourceRecordSets) Get(name string) ([]dnsprovider.ResourceRecordSet, error) { + rrsetList, err := r.List() + if err != nil { + return nil, err + } + + var recordSets []dnsprovider.ResourceRecordSet + for _, rrset := range rrsetList { + if rrset.Name() == name { + recordSets = append(recordSets, rrset) + } + } + + return recordSets, nil +} + +// New returns an implementation of dnsprovider.ResourceRecordSet. The name should contain the domain name. +func (r *resourceRecordSets) New(name string, rrdatas []string, ttl int64, rrstype rrstype.RrsType) dnsprovider.ResourceRecordSet { + if len(rrdatas) == 0 { + return nil + } + + return &resourceRecordSet{ + name: name, + data: rrdatas, + ttl: int(ttl), + recordType: rrstype, + } +} + +// StartChangeset returns an implementation of dnsprovider.ResourceRecordChangeset +func (r *resourceRecordSets) StartChangeset() dnsprovider.ResourceRecordChangeset { + return &resourceRecordChangeset{ + domainAPI: r.domainAPI, + zone: r.zone, + rrsets: r, + additions: []dnsprovider.ResourceRecordSet{}, + removals: []dnsprovider.ResourceRecordSet{}, + upserts: []dnsprovider.ResourceRecordSet{}, + } +} + +// Zone returns the associated implementation of dnsprovider.Zone +func (r *resourceRecordSets) Zone() dnsprovider.Zone { + return r.zone +} + +// recordRecordSet implements dnsprovider.ResourceRecordSet which represents +// a single record associated with a zone +type resourceRecordSet struct { + name string + data []string + ttl int + recordType rrstype.RrsType +} + +// Name returns the name of a resource record set +func (r *resourceRecordSet) Name() string { + return r.name +} + +// Rrdatas returns a list of data associated with a resource record set +func (r *resourceRecordSet) Rrdatas() []string { + return r.data +} + +// Ttl returns the time-to-live of a record +func (r *resourceRecordSet) Ttl() int64 { + return int64(r.ttl) +} + +// Type returns the type of record a resource record set is +func (r *resourceRecordSet) Type() rrstype.RrsType { + return r.recordType +} + +// resourceRecordChangeset implements dnsprovider.ResourceRecordChangeset +type resourceRecordChangeset struct { + domainAPI DomainAPI + zone *zone + rrsets dnsprovider.ResourceRecordSets + + additions []dnsprovider.ResourceRecordSet + removals []dnsprovider.ResourceRecordSet + upserts []dnsprovider.ResourceRecordSet +} + +// Add adds a new resource record set to the list of additions to apply +func (r *resourceRecordChangeset) Add(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset { + r.additions = append(r.additions, rrset) + return r +} + +// Remove adds a new resource record set to the list of removals to apply +func (r *resourceRecordChangeset) Remove(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset { + r.removals = append(r.removals, rrset) + return r +} + +// Upsert adds a new resource record set to the list of upserts to apply +func (r *resourceRecordChangeset) Upsert(rrset dnsprovider.ResourceRecordSet) dnsprovider.ResourceRecordChangeset { + r.upserts = append(r.upserts, rrset) + return r +} + +// Apply adds new records stored in r.additions, updates records stored in r.upserts and deletes records stored in r.removals +func (r *resourceRecordChangeset) Apply(ctx context.Context) error { + // Empty changesets should be a relatively quick no-op + if r.IsEmpty() { + klog.V(4).Info("record change set is empty") + return nil + } + + updateRecordsRequest := []*domain.RecordChange(nil) + klog.V(8).Infof("applying changes in record change set : [ %d additions | %d upserts | %d removals ]", + len(r.additions), len(r.upserts), len(r.removals)) + + records, err := listRecords(r.domainAPI, r.zone.Name()) + if err != nil { + return err + } + + if len(r.additions) > 0 { + recordsToAdd := []*domain.Record(nil) + for _, rrset := range r.additions { + recordName := strings.TrimSuffix(rrset.Name(), ".") + recordName = strings.TrimSuffix(recordName, "."+r.zone.Name()) + for _, rrdata := range rrset.Rrdatas() { + recordsToAdd = append(recordsToAdd, &domain.Record{ + Name: recordName, + Data: rrdata, + TTL: uint32(rrset.Ttl()), + Type: domain.RecordType(rrset.Type()), + }) + } + klog.V(8).Infof("adding new DNS record %q to zone %q", recordName, r.zone.name) + updateRecordsRequest = append(updateRecordsRequest, &domain.RecordChange{ + Add: &domain.RecordChangeAdd{ + Records: recordsToAdd, + }, + }) + } + } + + if len(r.upserts) > 0 { + for _, rrset := range r.upserts { + for _, rrdata := range rrset.Rrdatas() { + for _, record := range records { + recordNameWithZone := fmt.Sprintf("%s.%s.", record.Name, r.zone.Name()) + if recordNameWithZone == dns.EnsureDotSuffix(rrset.Name()) && rrset.Type() == rrstype.RrsType(record.Type) { + klog.V(8).Infof("changing DNS record %q of zone %q", record.Name, r.zone.Name()) + updateRecordsRequest = append(updateRecordsRequest, &domain.RecordChange{ + Set: &domain.RecordChangeSet{ + ID: &record.ID, + Records: []*domain.Record{ + { + Name: record.Name, + Data: rrdata, + TTL: uint32(rrset.Ttl()), + Type: domain.RecordType(rrset.Type()), + }, + }, + }, + }) + } + } + } + } + } + + if len(r.removals) > 0 { + for _, rrset := range r.removals { + for _, record := range records { + recordNameWithZone := fmt.Sprintf("%s.%s.", record.Name, r.zone.Name()) + if recordNameWithZone == dns.EnsureDotSuffix(rrset.Name()) && record.Data == rrset.Rrdatas()[0] && + rrset.Type() == rrstype.RrsType(record.Type) { + klog.V(8).Infof("removing DNS record %q of zone %q", record.Name, r.zone.name) + updateRecordsRequest = append(updateRecordsRequest, &domain.RecordChange{ + Delete: &domain.RecordChangeDelete{ + ID: &record.ID, + }, + }) + } + + } + } + } + + _, err = r.domainAPI.UpdateDNSZoneRecords(&domain.UpdateDNSZoneRecordsRequest{ + DNSZone: r.zone.Name(), + Changes: updateRecordsRequest, + }, scw.WithContext(ctx)) + if err != nil { + return fmt.Errorf("failed to apply resource record set: %w", err) + } + + klog.V(2).Info("record change sets successfully applied") + return nil +} + +// IsEmpty returns true if a changeset is empty, false otherwise +func (r *resourceRecordChangeset) IsEmpty() bool { + if len(r.additions) == 0 && len(r.removals) == 0 && len(r.upserts) == 0 { + return true + } + + return false +} + +// ResourceRecordSets returns the associated resourceRecordSets of a changeset +func (r *resourceRecordChangeset) ResourceRecordSets() dnsprovider.ResourceRecordSets { + return r.rrsets +} + +// listRecords returns a list of scaleway records given a zone name (the name of the record doesn't end with the zone name) +func listRecords(api DomainAPI, zoneName string) ([]*domain.Record, error) { + records, err := api.ListDNSZoneRecords(&domain.ListDNSZoneRecordsRequest{ + DNSZone: zoneName, + }, scw.WithAllPages()) + if err != nil { + return nil, fmt.Errorf("failed to list records: %w", err) + } + + return records.Records, err +} diff --git a/dnsprovider/pkg/dnsprovider/providers/scaleway/dns_test.go b/dnsprovider/pkg/dnsprovider/providers/scaleway/dns_test.go new file mode 100644 index 0000000000..13a6359332 --- /dev/null +++ b/dnsprovider/pkg/dnsprovider/providers/scaleway/dns_test.go @@ -0,0 +1,301 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dns + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/google/uuid" + domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1" + "k8s.io/kops/cloudmock/scaleway/mockdns" + "k8s.io/kops/dnsprovider/pkg/dnsprovider" + "k8s.io/kops/dnsprovider/pkg/dnsprovider/rrstype" +) + +func setUpFakeZones() *mockdns.FakeDomainAPI { + return &mockdns.FakeDomainAPI{ + DNSZones: []*domain.DNSZone{ + { + Domain: "example.com", + Subdomain: "zone", + }, + { + Domain: "example.com", + Subdomain: "kops", + }, + { + Domain: "domain.fr", + Subdomain: "zone", + }, + }, + } +} + +func setUpFakeZonesNil() *mockdns.FakeDomainAPI { + return &mockdns.FakeDomainAPI{} +} + +func getDNSProviderZones(api DomainAPI) dnsprovider.Zones { + dnsProvider := NewProvider(api) + zs, _ := dnsProvider.Zones() + return zs +} + +func TestZonesListValid(t *testing.T) { + domainAPI := setUpFakeZones() + z := &zones{domainAPI: domainAPI} + + zoneList, err := z.List() + + if err != nil { + t.Errorf("error listing zones: %v", err) + } + if len(zoneList) != 3 { + t.Errorf("expected at least 1 zone, got 0") + } + for i, zone := range zoneList { + if zone.Name() != domainAPI.DNSZones[i].Domain { + t.Errorf("expected %s as zone name, got: %s", domainAPI.DNSZones[i].Domain, zone.Name()) + } + } +} + +func TestZonesListShouldFail(t *testing.T) { + domainAPI := setUpFakeZonesNil() + z := &zones{domainAPI: domainAPI} + + zoneList, err := z.List() + + if err == nil { + t.Errorf("expected non-nil err") + } + if zoneList != nil { + t.Errorf("expected nil zone, got %v", zoneList) + } +} + +func TestAddValid(t *testing.T) { + domainAPI := setUpFakeZones() + zs := getDNSProviderZones(domainAPI) + + inZone := &zone{ + name: "dns.example.com", + domainAPI: domainAPI, + } + outZone, err := zs.Add(inZone) + + if err != nil { + t.Errorf("unexpected err: %v", err) + } + if outZone == nil { + t.Errorf("zone is nil, exiting test early") + } + if outZone.Name() != "dns" { + t.Errorf("unexpected zone name: %s", outZone.Name()) + } +} + +func TestAddShouldFail(t *testing.T) { + domainAPI := setUpFakeZonesNil() + zs := getDNSProviderZones(domainAPI) + + inZone := &zone{ + name: "dns.example.com", + domainAPI: domainAPI, + } + outZone, err := zs.Add(inZone) + + if outZone != nil { + t.Errorf("expected zone to be nil, got :%v", outZone) + } + if err == nil { + t.Errorf("expected non-nil err: %v", err) + } +} + +func TestRemoveValid(t *testing.T) { + domainAPI := setUpFakeZones() + zs := getDNSProviderZones(domainAPI) + + inZone := &zone{ + name: "kops.example.com", + domainAPI: domainAPI, + } + err := zs.Remove(inZone) + + if err != nil { + t.Errorf("unexpected err: %v", err) + } +} + +func TestRemoveShouldFail(t *testing.T) { + domainAPI := setUpFakeZonesNil() + zs := getDNSProviderZones(domainAPI) + + inZone := &zone{ + name: "kops.example.com", + domainAPI: domainAPI, + } + err := zs.Remove(inZone) + + if err == nil { + t.Errorf("expected non-nil err: %v", err) + } +} + +func TestNewZone(t *testing.T) { + domainAPI := setUpFakeZones() + zs := getDNSProviderZones(domainAPI) + + zone, err := zs.New("kops-dns-test") + + if err != nil { + t.Errorf("error creating zone: %v", err) + return + } + if zone == nil { + t.Errorf("zone is nil, exiting test early") + } + if zone.Name() != "kops-dns-test" { + t.Errorf("unexpected zone name: %v", zone.Name()) + } +} + +func setUpFakeRecords() *mockdns.FakeDomainAPI { + return &mockdns.FakeDomainAPI{ + Records: map[string]*domain.Record{ + "test": { + Data: "1.2.3.4", + Name: "test", + TTL: 3600, + Type: "A", + ID: uuid.New().String(), + }, + "to-remove": { + Data: "5.6.7.8", + Name: "to-remove", + TTL: 3600, + Type: "A", + ID: uuid.New().String(), + }, + "to-upsert": { + Data: "127.0.0.1", + Name: "to-upsert", + TTL: 3600, + Type: "A", + ID: uuid.New().String(), + }, + }, + } +} + +func TestNewResourceRecordSet(t *testing.T) { + domainAPI := setUpFakeRecords() + zoneName := "kops.example.com" + zone := zone{ + domainAPI: domainAPI, + name: zoneName, + } + + rrset, _ := zone.ResourceRecordSets() + rrsets, err := rrset.List() + + if err != nil { + t.Errorf("error listing resource record sets: %v", err) + } + if len(rrsets) != 3 { + t.Errorf("unexpected number of records: %d", len(rrsets)) + } + + for _, record := range rrsets { + recordNameShort := strings.TrimSuffix(record.Name(), "."+zoneName) + expectedRecord, ok := domainAPI.Records[recordNameShort] + if !ok { + t.Errorf("could not find record %s in mock records list", record.Name()) + } + + expectedName := fmt.Sprintf("%s.%s", expectedRecord.Name, zoneName) + if record.Name() != expectedName { + t.Errorf("expected %q as record name, got %q", expectedName, record.Name()) + } + if record.Ttl() != int64(expectedRecord.TTL) { + t.Errorf("expected %d as record TTL, got %d", expectedRecord.TTL, record.Ttl()) + } + if record.Type() != rrstype.RrsType(expectedRecord.Type) { + t.Errorf("expected %q as record type, got %q", expectedRecord.Type, record.Type()) + } + if len(record.Rrdatas()) < 1 { + t.Errorf("expected at least 1 rrdata for record %s, got 0", record.Name()) + } else if record.Rrdatas()[0] != expectedRecord.Data { + t.Errorf("expected %q as record data, got %q", expectedRecord.Data, record.Rrdatas()) + } + } +} + +func TestResourceRecordChangeset(t *testing.T) { + ctx := context.Background() + domainAPI := setUpFakeRecords() + zoneName := "kops.example.com" + zone := zone{ + domainAPI: domainAPI, + name: zoneName, + } + + rrsets, _ := zone.ResourceRecordSets() + changeset := rrsets.StartChangeset() + + if !changeset.IsEmpty() { + t.Error("expected empty changeset") + } + + rrset := rrsets.New(fmt.Sprintf("%s.%s", "to-add", zoneName), []string{"127.0.0.1"}, 3600, rrstype.A) + changeset.Add(rrset) + + rrset = rrsets.New(fmt.Sprintf("%s.%s", "to-remove", zoneName), []string{"5.6.7.8"}, 3600, rrstype.A) + changeset.Remove(rrset) + + rrset = rrsets.New(fmt.Sprintf("%s.%s", "to-upsert", zoneName), []string{"127.0.0.1"}, 3601, rrstype.A) + changeset.Upsert(rrset) + + err := changeset.Apply(ctx) + if err != nil { + t.Errorf("error applying changeset: %v", err) + } + + _, err = rrsets.Get(fmt.Sprintf("%s.%s", "test", zoneName)) + if err != nil { + t.Errorf("unexpected error getting resource record set: %v", err) + } + _, err = rrsets.Get(fmt.Sprintf("%s.%s", "to-add", zoneName)) + if err != nil { + t.Errorf("unexpected error getting resource record set: %v", err) + } + recordsRemove, _ := rrsets.Get(fmt.Sprintf("%s.%s", "to-remove", zoneName)) + if recordsRemove != nil { + t.Errorf("record set 'to-remove' should have been deleted") + } + recordsUpsert, err := rrsets.Get(fmt.Sprintf("%s.%s", "to-upsert", zoneName)) + if err != nil { + t.Errorf("unexpected error getting resource record set: %v", err) + } + if recordsUpsert[0].Ttl() != 3601 { + t.Errorf("unexpected record TTL: %d, expected 3601", recordsUpsert[0].Ttl()) + } +} diff --git a/dnsprovider/pkg/dnsprovider/providers/scaleway/domain_api_interface.go b/dnsprovider/pkg/dnsprovider/providers/scaleway/domain_api_interface.go new file mode 100644 index 0000000000..086496c1ba --- /dev/null +++ b/dnsprovider/pkg/dnsprovider/providers/scaleway/domain_api_interface.go @@ -0,0 +1,34 @@ +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package dns + +import ( + domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1" + "github.com/scaleway/scaleway-sdk-go/scw" + "k8s.io/kops/cloudmock/scaleway/mockdns" +) + +type DomainAPI interface { + ListDNSZones(req *domain.ListDNSZonesRequest, opts ...scw.RequestOption) (*domain.ListDNSZonesResponse, error) + CreateDNSZone(req *domain.CreateDNSZoneRequest, opts ...scw.RequestOption) (*domain.DNSZone, error) + DeleteDNSZone(req *domain.DeleteDNSZoneRequest, opts ...scw.RequestOption) (*domain.DeleteDNSZoneResponse, error) + ListDNSZoneRecords(req *domain.ListDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.ListDNSZoneRecordsResponse, error) + UpdateDNSZoneRecords(req *domain.UpdateDNSZoneRecordsRequest, opts ...scw.RequestOption) (*domain.UpdateDNSZoneRecordsResponse, error) +} + +var _ DomainAPI = &domain.API{} +var _ DomainAPI = &mockdns.FakeDomainAPI{} diff --git a/docs/getting_started/scaleway.md b/docs/getting_started/scaleway.md index 6bb4704507..5f9fc469e5 100644 --- a/docs/getting_started/scaleway.md +++ b/docs/getting_started/scaleway.md @@ -10,15 +10,14 @@ ### Coming soon -* Scaleway DNS (to create clusters with a custom domain name) +* [Terraform](https://github.com/scaleway/terraform-provider-scaleway) support * Private network ### Next features to implement * `kops rolling-update` -* BareMetal servers -* [Terraform](https://github.com/scaleway/terraform-provider-scaleway) support * [Autoscaler](https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler/cloudprovider/scaleway) support +* BareMetal servers ## Requirements @@ -30,7 +29,7 @@ ### Optional * [SSH key](https://www.scaleway.com/en/docs/configure-new-ssh-key/) : creating a cluster can be done without an SSH key, but it is required to update it. `id_rsa` and `id_ed25519` keys are supported - +* [Domain name](https://www.scaleway.com/en/docs/network/domains-and-dns/quickstart/) : if you want to host your cluster on your own domain, you will have to register it with Scaleway. ## Environment Variables @@ -65,6 +64,8 @@ Note that for now you can only create a kops cluster in a single availability zo kops create cluster --cloud=scaleway --name=mycluster.k8s.local --zones=fr-par-1 --yes # This creates a cluster with no DNS in zone nl-ams-2 kops create cluster --cloud=scaleway --name=my.cluster --zones=nl-ams-2 --yes + # This creates a cluster with the Scaleway DNS (on a domain name that you own and have registered with Scaleway) in zone pl-waw-1 +kops create cluster --cloud=scaleway --name=mycluster.mydomain.com --zones=pl-waw-1 --yes ``` ### Editing your cluster diff --git a/pkg/resources/scaleway/resources.go b/pkg/resources/scaleway/resources.go index ab2dfd3e5e..e7ded710f8 100644 --- a/pkg/resources/scaleway/resources.go +++ b/pkg/resources/scaleway/resources.go @@ -17,6 +17,9 @@ limitations under the License. package scaleway import ( + "strings" + + domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1" "k8s.io/kops/pkg/resources" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/cloudup/scaleway" @@ -27,6 +30,7 @@ import ( ) const ( + resourceTypeDNSRecord = "dns-record" resourceTypeLoadBalancer = "load-balancer" resourceTypeServer = "server" resourceTypeSSHKey = "ssh-key" @@ -45,6 +49,9 @@ func ListResources(cloud scaleway.ScwCloud, clusterInfo resources.ClusterInfo) ( listSSHKeys, listVolumes, } + if !strings.HasSuffix(clusterName, ".k8s.local") && !clusterInfo.UsesNoneDNS { + listFunctions = append(listFunctions, listDNSRecords) + } for _, fn := range listFunctions { rt, err := fn(cloud, clusterName) @@ -59,6 +66,30 @@ func ListResources(cloud scaleway.ScwCloud, clusterInfo resources.ClusterInfo) ( return resourceTrackers, nil } +func listDNSRecords(cloud fi.Cloud, clusterName string) ([]*resources.Resource, error) { + c := cloud.(scaleway.ScwCloud) + records, err := c.GetClusterDNSRecords(clusterName) + if err != nil { + return nil, err + } + + resourceTrackers := []*resources.Resource(nil) + for _, record := range records { + resourceTracker := &resources.Resource{ + Name: record.Name, + ID: record.ID, + Type: resourceTypeDNSRecord, + Deleter: func(cloud fi.Cloud, tracker *resources.Resource) error { + return deleteDNSRecord(cloud, tracker, clusterName) + }, + Obj: record, + } + resourceTrackers = append(resourceTrackers, resourceTracker) + } + + return resourceTrackers, nil +} + func listLoadBalancers(cloud fi.Cloud, clusterName string) ([]*resources.Resource, error) { c := cloud.(scaleway.ScwCloud) lbs, err := c.GetClusterLoadBalancers(clusterName) @@ -158,6 +189,13 @@ func listVolumes(cloud fi.Cloud, clusterName string) ([]*resources.Resource, err return resourceTrackers, nil } +func deleteDNSRecord(cloud fi.Cloud, tracker *resources.Resource, domainName string) error { + c := cloud.(scaleway.ScwCloud) + record := tracker.Obj.(*domain.Record) + + return c.DeleteDNSRecord(record, domainName) +} + func deleteLoadBalancer(cloud fi.Cloud, tracker *resources.Resource) error { c := cloud.(scaleway.ScwCloud) loadBalancer := tracker.Obj.(*lb.LB) diff --git a/tests/integration/update_cluster/minimal_scaleway/data/aws_s3_object_scw-minimal.k8s.local-addons-bootstrap_content b/tests/integration/update_cluster/minimal_scaleway/data/aws_s3_object_scw-minimal.k8s.local-addons-bootstrap_content index f7aeef4d13..b177e39050 100644 --- a/tests/integration/update_cluster/minimal_scaleway/data/aws_s3_object_scw-minimal.k8s.local-addons-bootstrap_content +++ b/tests/integration/update_cluster/minimal_scaleway/data/aws_s3_object_scw-minimal.k8s.local-addons-bootstrap_content @@ -34,7 +34,7 @@ spec: version: 9.99.0 - id: k8s-1.12 manifest: dns-controller.addons.k8s.io/k8s-1.12.yaml - manifestHash: 304cbbd52a3dbff42b8302023297ce34cae0da69ac0884e8cff0d9f1b5c74028 + manifestHash: 4814c76684eae04bb46a11f7f2a9485aedc41200205b9216020dc7e8cdc73ded name: dns-controller.addons.k8s.io selector: k8s-addon: dns-controller.addons.k8s.io diff --git a/tests/integration/update_cluster/minimal_scaleway/data/aws_s3_object_scw-minimal.k8s.local-addons-dns-controller.addons.k8s.io-k8s-1.12_content b/tests/integration/update_cluster/minimal_scaleway/data/aws_s3_object_scw-minimal.k8s.local-addons-dns-controller.addons.k8s.io-k8s-1.12_content index ec7f44b1b0..5dd08061e5 100644 --- a/tests/integration/update_cluster/minimal_scaleway/data/aws_s3_object_scw-minimal.k8s.local-addons-dns-controller.addons.k8s.io-k8s-1.12_content +++ b/tests/integration/update_cluster/minimal_scaleway/data/aws_s3_object_scw-minimal.k8s.local-addons-dns-controller.addons.k8s.io-k8s-1.12_content @@ -51,6 +51,9 @@ spec: env: - name: KUBERNETES_SERVICE_HOST value: 127.0.0.1 + envFrom: + - secretRef: + name: scaleway-secret image: registry.k8s.io/kops/dns-controller:1.27.0-beta.1 name: dns-controller resources: diff --git a/upup/models/cloudup/resources/addons/dns-controller.addons.k8s.io/k8s-1.12.yaml.template b/upup/models/cloudup/resources/addons/dns-controller.addons.k8s.io/k8s-1.12.yaml.template index ae1143d60b..3dc548dae6 100644 --- a/upup/models/cloudup/resources/addons/dns-controller.addons.k8s.io/k8s-1.12.yaml.template +++ b/upup/models/cloudup/resources/addons/dns-controller.addons.k8s.io/k8s-1.12.yaml.template @@ -72,6 +72,11 @@ spec: secretKeyRef: name: digitalocean key: access-token +{{- end }} +{{- if eq GetCloudProvider "scaleway" }} + envFrom: + - secretRef: + name: scaleway-secret {{- end }} resources: requests: diff --git a/upup/pkg/fi/cloudup/scaleway/cloud.go b/upup/pkg/fi/cloudup/scaleway/cloud.go index eaa852e980..b4b1cd205c 100644 --- a/upup/pkg/fi/cloudup/scaleway/cloud.go +++ b/upup/pkg/fi/cloudup/scaleway/cloud.go @@ -21,6 +21,7 @@ import ( "os" "strings" + domain "github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1" iam "github.com/scaleway/scaleway-sdk-go/api/iam/v1alpha1" "github.com/scaleway/scaleway-sdk-go/api/instance/v1" "github.com/scaleway/scaleway-sdk-go/api/lb/v1" @@ -29,6 +30,7 @@ import ( "k8s.io/klog/v2" kopsv "k8s.io/kops" "k8s.io/kops/dnsprovider/pkg/dnsprovider" + dns "k8s.io/kops/dnsprovider/pkg/dnsprovider/providers/scaleway" "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/cloudinstances" "k8s.io/kops/upup/pkg/fi" @@ -54,6 +56,7 @@ type ScwCloud interface { Region() string Zone() string + DomainService() *domain.API IamService() *iam.API InstanceService() *instance.API LBService() *lb.ZonedAPI @@ -67,11 +70,13 @@ type ScwCloud interface { GetApiIngressStatus(cluster *kops.Cluster) ([]fi.ApiIngressStatus, error) GetCloudGroups(cluster *kops.Cluster, instancegroups []*kops.InstanceGroup, warnUnmatched bool, nodes []v1.Node) (map[string]*cloudinstances.CloudInstanceGroup, error) + GetClusterDNSRecords(clusterName string) ([]*domain.Record, error) GetClusterLoadBalancers(clusterName string) ([]*lb.LB, error) GetClusterServers(clusterName string, instanceGroupName *string) ([]*instance.Server, error) GetClusterSSHKeys(clusterName string) ([]*iam.SSHKey, error) GetClusterVolumes(clusterName string) ([]*instance.Volume, error) + DeleteDNSRecord(record *domain.Record, clusterName string) error DeleteLoadBalancer(loadBalancer *lb.LB) error DeleteServer(server *instance.Server) error DeleteSSHKey(sshkey *iam.SSHKey) error @@ -86,8 +91,10 @@ type scwCloudImplementation struct { client *scw.Client region scw.Region zone scw.Zone + dns dnsprovider.Interface tags map[string]string + domainAPI *domain.API iamAPI *iam.API instanceAPI *instance.API lbAPI *lb.ZonedAPI @@ -130,7 +137,9 @@ func NewScwCloud(tags map[string]string) (ScwCloud, error) { client: scwClient, region: region, zone: zone, + dns: dns.NewProvider(domain.NewAPI(scwClient)), tags: tags, + domainAPI: domain.NewAPI(scwClient), iamAPI: iam.NewAPI(scwClient), instanceAPI: instance.NewAPI(scwClient), lbAPI: lb.NewZonedAPI(scwClient), @@ -147,8 +156,11 @@ func (s *scwCloudImplementation) ClusterName(tags []string) string { } func (s *scwCloudImplementation) DNS() (dnsprovider.Interface, error) { - klog.V(8).Infof("Scaleway DNS is not implemented yet") - return nil, fmt.Errorf("DNS is not implemented yet for Scaleway") + provider, err := dnsprovider.GetDnsProvider(dns.ProviderName, nil) + if err != nil { + return nil, fmt.Errorf("error building DNS provider: %w", err) + } + return provider, nil } func (s *scwCloudImplementation) ProviderID() kops.CloudProviderID { @@ -163,6 +175,10 @@ func (s *scwCloudImplementation) Zone() string { return string(s.zone) } +func (s *scwCloudImplementation) DomainService() *domain.API { + return s.domainAPI +} + func (s *scwCloudImplementation) IamService() *iam.API { return s.iamAPI } @@ -373,6 +389,27 @@ func buildCloudGroup(ig *kops.InstanceGroup, sg []*instance.Server, nodeMap map[ return cloudInstanceGroup, nil } +func (s *scwCloudImplementation) GetClusterDNSRecords(clusterName string) ([]*domain.Record, error) { + names := strings.SplitN(clusterName, ".", 2) + clusterNameShort := names[0] + domainName := names[1] + + records, err := s.domainAPI.ListDNSZoneRecords(&domain.ListDNSZoneRecordsRequest{ + DNSZone: domainName, + }, scw.WithAllPages()) + if err != nil { + return nil, fmt.Errorf("listing cluster DNS records: %w", err) + } + + clusterDNSRecords := []*domain.Record(nil) + for _, record := range records.Records { + if strings.HasSuffix(record.Name, clusterNameShort) { + clusterDNSRecords = append(clusterDNSRecords, record) + } + } + return clusterDNSRecords, nil +} + func (s *scwCloudImplementation) GetClusterLoadBalancers(clusterName string) ([]*lb.LB, error) { loadBalancerName := "api." + clusterName lbs, err := s.lbAPI.ListLBs(&lb.ZonedAPIListLBsRequest{ @@ -430,6 +467,29 @@ func (s *scwCloudImplementation) GetClusterVolumes(clusterName string) ([]*insta return volumes.Volumes, nil } +func (s *scwCloudImplementation) DeleteDNSRecord(record *domain.Record, clusterName string) error { + domainName := strings.SplitN(clusterName, ".", 2)[1] + recordDeleteRequest := &domain.UpdateDNSZoneRecordsRequest{ + DNSZone: domainName, + Changes: []*domain.RecordChange{ + { + Delete: &domain.RecordChangeDelete{ + ID: scw.StringPtr(record.ID), + }, + }, + }, + } + _, err := s.domainAPI.UpdateDNSZoneRecords(recordDeleteRequest) + if err != nil { + if is404Error(err) { + klog.V(8).Infof("DNS record %q (%s) was already deleted", record.Name, record.ID) + return nil + } + return fmt.Errorf("failed to delete record %s: %w", record.Name, err) + } + return nil +} + func (s *scwCloudImplementation) DeleteLoadBalancer(loadBalancer *lb.LB) error { ipsToRelease := loadBalancer.IP diff --git a/upup/pkg/fi/cloudup/template_functions.go b/upup/pkg/fi/cloudup/template_functions.go index 2bf4056f93..e7d7fc4b8e 100644 --- a/upup/pkg/fi/cloudup/template_functions.go +++ b/upup/pkg/fi/cloudup/template_functions.go @@ -613,6 +613,8 @@ func (tf *TemplateFunctions) DNSControllerArgv() ([]string, error) { argv = append(argv, "--dns=digitalocean") case kops.CloudProviderOpenstack: argv = append(argv, "--dns=openstack-designate") + case kops.CloudProviderScaleway: + argv = append(argv, "--dns=scaleway") default: return nil, fmt.Errorf("unhandled cloudprovider %q", cluster.Spec.GetCloudProvider()) diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1/domain_sdk.go b/vendor/github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1/domain_sdk.go new file mode 100644 index 0000000000..eff0f0c67f --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1/domain_sdk.go @@ -0,0 +1,3916 @@ +// This file was automatically generated. DO NOT EDIT. +// If you have any remark or suggestion do not hesitate to open an issue. + +// Package domain provides methods and message types of the domain v2beta1 API. +package domain + +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "net/http" + "net/url" + "strings" + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/internal/marshaler" + "github.com/scaleway/scaleway-sdk-go/internal/parameter" + "github.com/scaleway/scaleway-sdk-go/namegenerator" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +// always import dependencies +var ( + _ fmt.Stringer + _ json.Unmarshaler + _ url.URL + _ net.IP + _ http.Header + _ bytes.Reader + _ time.Time + _ = strings.Join + + _ scw.ScalewayRequest + _ marshaler.Duration + _ scw.File + _ = parameter.AddToQuery + _ = namegenerator.GetRandomName +) + +// API: domains and DNS API. +// Manage your domains, DNS zones and records with the Domains and DNS API. +type API struct { + client *scw.Client +} + +// NewAPI returns a API object from a Scaleway client. +func NewAPI(client *scw.Client) *API { + return &API{ + client: client, + } +} + +// RegistrarAPI: domains and DNS - Registrar API. +// Manage your domains and contacts. +type RegistrarAPI struct { + client *scw.Client +} + +// NewRegistrarAPI returns a RegistrarAPI object from a Scaleway client. +func NewRegistrarAPI(client *scw.Client) *RegistrarAPI { + return &RegistrarAPI{ + client: client, + } +} + +type ContactEmailStatus string + +const ( + ContactEmailStatusEmailStatusUnknown = ContactEmailStatus("email_status_unknown") + ContactEmailStatusValidated = ContactEmailStatus("validated") + ContactEmailStatusNotValidated = ContactEmailStatus("not_validated") + ContactEmailStatusInvalidEmail = ContactEmailStatus("invalid_email") +) + +func (enum ContactEmailStatus) String() string { + if enum == "" { + // return default value if empty + return "email_status_unknown" + } + return string(enum) +} + +func (enum ContactEmailStatus) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ContactEmailStatus) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ContactEmailStatus(ContactEmailStatus(tmp).String()) + return nil +} + +type ContactExtensionFRMode string + +const ( + ContactExtensionFRModeModeUnknown = ContactExtensionFRMode("mode_unknown") + ContactExtensionFRModeIndividual = ContactExtensionFRMode("individual") + ContactExtensionFRModeCompanyIdentificationCode = ContactExtensionFRMode("company_identification_code") + ContactExtensionFRModeDuns = ContactExtensionFRMode("duns") + ContactExtensionFRModeLocal = ContactExtensionFRMode("local") + ContactExtensionFRModeAssociation = ContactExtensionFRMode("association") + ContactExtensionFRModeTrademark = ContactExtensionFRMode("trademark") + ContactExtensionFRModeCodeAuthAfnic = ContactExtensionFRMode("code_auth_afnic") +) + +func (enum ContactExtensionFRMode) String() string { + if enum == "" { + // return default value if empty + return "mode_unknown" + } + return string(enum) +} + +func (enum ContactExtensionFRMode) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ContactExtensionFRMode) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ContactExtensionFRMode(ContactExtensionFRMode(tmp).String()) + return nil +} + +type ContactExtensionNLLegalForm string + +const ( + ContactExtensionNLLegalFormLegalFormUnknown = ContactExtensionNLLegalForm("legal_form_unknown") + ContactExtensionNLLegalFormOther = ContactExtensionNLLegalForm("other") + ContactExtensionNLLegalFormNonDutchEuCompany = ContactExtensionNLLegalForm("non_dutch_eu_company") + ContactExtensionNLLegalFormNonDutchLegalFormEnterpriseSubsidiary = ContactExtensionNLLegalForm("non_dutch_legal_form_enterprise_subsidiary") + ContactExtensionNLLegalFormLimitedCompany = ContactExtensionNLLegalForm("limited_company") + ContactExtensionNLLegalFormLimitedCompanyInFormation = ContactExtensionNLLegalForm("limited_company_in_formation") + ContactExtensionNLLegalFormCooperative = ContactExtensionNLLegalForm("cooperative") + ContactExtensionNLLegalFormLimitedPartnership = ContactExtensionNLLegalForm("limited_partnership") + ContactExtensionNLLegalFormSoleCompany = ContactExtensionNLLegalForm("sole_company") + ContactExtensionNLLegalFormEuropeanEconomicInterestGroup = ContactExtensionNLLegalForm("european_economic_interest_group") + ContactExtensionNLLegalFormReligiousEntity = ContactExtensionNLLegalForm("religious_entity") + ContactExtensionNLLegalFormPartnership = ContactExtensionNLLegalForm("partnership") + ContactExtensionNLLegalFormPublicCompany = ContactExtensionNLLegalForm("public_company") + ContactExtensionNLLegalFormMutualBenefitCompany = ContactExtensionNLLegalForm("mutual_benefit_company") + ContactExtensionNLLegalFormResidential = ContactExtensionNLLegalForm("residential") + ContactExtensionNLLegalFormShippingCompany = ContactExtensionNLLegalForm("shipping_company") + ContactExtensionNLLegalFormFoundation = ContactExtensionNLLegalForm("foundation") + ContactExtensionNLLegalFormAssociation = ContactExtensionNLLegalForm("association") + ContactExtensionNLLegalFormTradingPartnership = ContactExtensionNLLegalForm("trading_partnership") + ContactExtensionNLLegalFormNaturalPerson = ContactExtensionNLLegalForm("natural_person") +) + +func (enum ContactExtensionNLLegalForm) String() string { + if enum == "" { + // return default value if empty + return "legal_form_unknown" + } + return string(enum) +} + +func (enum ContactExtensionNLLegalForm) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ContactExtensionNLLegalForm) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ContactExtensionNLLegalForm(ContactExtensionNLLegalForm(tmp).String()) + return nil +} + +type ContactLegalForm string + +const ( + ContactLegalFormLegalFormUnknown = ContactLegalForm("legal_form_unknown") + ContactLegalFormIndividual = ContactLegalForm("individual") + ContactLegalFormCorporate = ContactLegalForm("corporate") + ContactLegalFormAssociation = ContactLegalForm("association") + ContactLegalFormOther = ContactLegalForm("other") +) + +func (enum ContactLegalForm) String() string { + if enum == "" { + // return default value if empty + return "legal_form_unknown" + } + return string(enum) +} + +func (enum ContactLegalForm) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ContactLegalForm) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ContactLegalForm(ContactLegalForm(tmp).String()) + return nil +} + +type DNSZoneStatus string + +const ( + DNSZoneStatusUnknown = DNSZoneStatus("unknown") + DNSZoneStatusActive = DNSZoneStatus("active") + DNSZoneStatusPending = DNSZoneStatus("pending") + DNSZoneStatusError = DNSZoneStatus("error") + DNSZoneStatusLocked = DNSZoneStatus("locked") +) + +func (enum DNSZoneStatus) String() string { + if enum == "" { + // return default value if empty + return "unknown" + } + return string(enum) +} + +func (enum DNSZoneStatus) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *DNSZoneStatus) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = DNSZoneStatus(DNSZoneStatus(tmp).String()) + return nil +} + +type DSRecordAlgorithm string + +const ( + DSRecordAlgorithmRsamd5 = DSRecordAlgorithm("rsamd5") + DSRecordAlgorithmDh = DSRecordAlgorithm("dh") + DSRecordAlgorithmDsa = DSRecordAlgorithm("dsa") + DSRecordAlgorithmRsasha1 = DSRecordAlgorithm("rsasha1") + DSRecordAlgorithmDsaNsec3Sha1 = DSRecordAlgorithm("dsa_nsec3_sha1") + DSRecordAlgorithmRsasha1Nsec3Sha1 = DSRecordAlgorithm("rsasha1_nsec3_sha1") + DSRecordAlgorithmRsasha256 = DSRecordAlgorithm("rsasha256") + DSRecordAlgorithmRsasha512 = DSRecordAlgorithm("rsasha512") + DSRecordAlgorithmEccGost = DSRecordAlgorithm("ecc_gost") + DSRecordAlgorithmEcdsap256sha256 = DSRecordAlgorithm("ecdsap256sha256") + DSRecordAlgorithmEcdsap384sha384 = DSRecordAlgorithm("ecdsap384sha384") + DSRecordAlgorithmEd25519 = DSRecordAlgorithm("ed25519") + DSRecordAlgorithmEd448 = DSRecordAlgorithm("ed448") +) + +func (enum DSRecordAlgorithm) String() string { + if enum == "" { + // return default value if empty + return "rsamd5" + } + return string(enum) +} + +func (enum DSRecordAlgorithm) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *DSRecordAlgorithm) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = DSRecordAlgorithm(DSRecordAlgorithm(tmp).String()) + return nil +} + +type DSRecordDigestType string + +const ( + DSRecordDigestTypeSha1 = DSRecordDigestType("sha_1") + DSRecordDigestTypeSha256 = DSRecordDigestType("sha_256") + DSRecordDigestTypeGostR34_11_94 = DSRecordDigestType("gost_r_34_11_94") + DSRecordDigestTypeSha384 = DSRecordDigestType("sha_384") +) + +func (enum DSRecordDigestType) String() string { + if enum == "" { + // return default value if empty + return "sha_1" + } + return string(enum) +} + +func (enum DSRecordDigestType) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *DSRecordDigestType) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = DSRecordDigestType(DSRecordDigestType(tmp).String()) + return nil +} + +type DomainFeatureStatus string + +const ( + DomainFeatureStatusFeatureStatusUnknown = DomainFeatureStatus("feature_status_unknown") + DomainFeatureStatusEnabling = DomainFeatureStatus("enabling") + DomainFeatureStatusEnabled = DomainFeatureStatus("enabled") + DomainFeatureStatusDisabling = DomainFeatureStatus("disabling") + DomainFeatureStatusDisabled = DomainFeatureStatus("disabled") +) + +func (enum DomainFeatureStatus) String() string { + if enum == "" { + // return default value if empty + return "feature_status_unknown" + } + return string(enum) +} + +func (enum DomainFeatureStatus) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *DomainFeatureStatus) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = DomainFeatureStatus(DomainFeatureStatus(tmp).String()) + return nil +} + +type DomainRegistrationStatusTransferStatus string + +const ( + DomainRegistrationStatusTransferStatusStatusUnknown = DomainRegistrationStatusTransferStatus("status_unknown") + DomainRegistrationStatusTransferStatusPending = DomainRegistrationStatusTransferStatus("pending") + DomainRegistrationStatusTransferStatusWaitingVote = DomainRegistrationStatusTransferStatus("waiting_vote") + DomainRegistrationStatusTransferStatusRejected = DomainRegistrationStatusTransferStatus("rejected") + DomainRegistrationStatusTransferStatusProcessing = DomainRegistrationStatusTransferStatus("processing") + DomainRegistrationStatusTransferStatusDone = DomainRegistrationStatusTransferStatus("done") +) + +func (enum DomainRegistrationStatusTransferStatus) String() string { + if enum == "" { + // return default value if empty + return "status_unknown" + } + return string(enum) +} + +func (enum DomainRegistrationStatusTransferStatus) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *DomainRegistrationStatusTransferStatus) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = DomainRegistrationStatusTransferStatus(DomainRegistrationStatusTransferStatus(tmp).String()) + return nil +} + +type DomainStatus string + +const ( + DomainStatusStatusUnknown = DomainStatus("status_unknown") + DomainStatusActive = DomainStatus("active") + DomainStatusCreating = DomainStatus("creating") + DomainStatusCreateError = DomainStatus("create_error") + DomainStatusRenewing = DomainStatus("renewing") + DomainStatusRenewError = DomainStatus("renew_error") + DomainStatusXfering = DomainStatus("xfering") + DomainStatusXferError = DomainStatus("xfer_error") + DomainStatusExpired = DomainStatus("expired") + DomainStatusExpiring = DomainStatus("expiring") + DomainStatusUpdating = DomainStatus("updating") + DomainStatusChecking = DomainStatus("checking") + DomainStatusLocked = DomainStatus("locked") + DomainStatusDeleting = DomainStatus("deleting") +) + +func (enum DomainStatus) String() string { + if enum == "" { + // return default value if empty + return "status_unknown" + } + return string(enum) +} + +func (enum DomainStatus) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *DomainStatus) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = DomainStatus(DomainStatus(tmp).String()) + return nil +} + +type HostStatus string + +const ( + HostStatusUnknownStatus = HostStatus("unknown_status") + HostStatusActive = HostStatus("active") + HostStatusUpdating = HostStatus("updating") + HostStatusDeleting = HostStatus("deleting") +) + +func (enum HostStatus) String() string { + if enum == "" { + // return default value if empty + return "unknown_status" + } + return string(enum) +} + +func (enum HostStatus) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *HostStatus) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = HostStatus(HostStatus(tmp).String()) + return nil +} + +type LanguageCode string + +const ( + LanguageCodeUnknownLanguageCode = LanguageCode("unknown_language_code") + LanguageCodeEnUS = LanguageCode("en_US") + LanguageCodeFrFR = LanguageCode("fr_FR") + LanguageCodeDeDE = LanguageCode("de_DE") +) + +func (enum LanguageCode) String() string { + if enum == "" { + // return default value if empty + return "unknown_language_code" + } + return string(enum) +} + +func (enum LanguageCode) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *LanguageCode) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = LanguageCode(LanguageCode(tmp).String()) + return nil +} + +type ListDNSZoneRecordsRequestOrderBy string + +const ( + ListDNSZoneRecordsRequestOrderByNameAsc = ListDNSZoneRecordsRequestOrderBy("name_asc") + ListDNSZoneRecordsRequestOrderByNameDesc = ListDNSZoneRecordsRequestOrderBy("name_desc") +) + +func (enum ListDNSZoneRecordsRequestOrderBy) String() string { + if enum == "" { + // return default value if empty + return "name_asc" + } + return string(enum) +} + +func (enum ListDNSZoneRecordsRequestOrderBy) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ListDNSZoneRecordsRequestOrderBy) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ListDNSZoneRecordsRequestOrderBy(ListDNSZoneRecordsRequestOrderBy(tmp).String()) + return nil +} + +type ListDNSZonesRequestOrderBy string + +const ( + ListDNSZonesRequestOrderByDomainAsc = ListDNSZonesRequestOrderBy("domain_asc") + ListDNSZonesRequestOrderByDomainDesc = ListDNSZonesRequestOrderBy("domain_desc") + ListDNSZonesRequestOrderBySubdomainAsc = ListDNSZonesRequestOrderBy("subdomain_asc") + ListDNSZonesRequestOrderBySubdomainDesc = ListDNSZonesRequestOrderBy("subdomain_desc") +) + +func (enum ListDNSZonesRequestOrderBy) String() string { + if enum == "" { + // return default value if empty + return "domain_asc" + } + return string(enum) +} + +func (enum ListDNSZonesRequestOrderBy) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ListDNSZonesRequestOrderBy) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ListDNSZonesRequestOrderBy(ListDNSZonesRequestOrderBy(tmp).String()) + return nil +} + +type ListDomainsRequestOrderBy string + +const ( + ListDomainsRequestOrderByDomainAsc = ListDomainsRequestOrderBy("domain_asc") + ListDomainsRequestOrderByDomainDesc = ListDomainsRequestOrderBy("domain_desc") +) + +func (enum ListDomainsRequestOrderBy) String() string { + if enum == "" { + // return default value if empty + return "domain_asc" + } + return string(enum) +} + +func (enum ListDomainsRequestOrderBy) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ListDomainsRequestOrderBy) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ListDomainsRequestOrderBy(ListDomainsRequestOrderBy(tmp).String()) + return nil +} + +type ListRenewableDomainsRequestOrderBy string + +const ( + ListRenewableDomainsRequestOrderByDomainAsc = ListRenewableDomainsRequestOrderBy("domain_asc") + ListRenewableDomainsRequestOrderByDomainDesc = ListRenewableDomainsRequestOrderBy("domain_desc") +) + +func (enum ListRenewableDomainsRequestOrderBy) String() string { + if enum == "" { + // return default value if empty + return "domain_asc" + } + return string(enum) +} + +func (enum ListRenewableDomainsRequestOrderBy) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ListRenewableDomainsRequestOrderBy) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ListRenewableDomainsRequestOrderBy(ListRenewableDomainsRequestOrderBy(tmp).String()) + return nil +} + +type ListTasksRequestOrderBy string + +const ( + ListTasksRequestOrderByDomainDesc = ListTasksRequestOrderBy("domain_desc") + ListTasksRequestOrderByDomainAsc = ListTasksRequestOrderBy("domain_asc") + ListTasksRequestOrderByTypeAsc = ListTasksRequestOrderBy("type_asc") + ListTasksRequestOrderByTypeDesc = ListTasksRequestOrderBy("type_desc") + ListTasksRequestOrderByStatusAsc = ListTasksRequestOrderBy("status_asc") + ListTasksRequestOrderByStatusDesc = ListTasksRequestOrderBy("status_desc") + ListTasksRequestOrderByUpdatedAtAsc = ListTasksRequestOrderBy("updated_at_asc") + ListTasksRequestOrderByUpdatedAtDesc = ListTasksRequestOrderBy("updated_at_desc") +) + +func (enum ListTasksRequestOrderBy) String() string { + if enum == "" { + // return default value if empty + return "domain_desc" + } + return string(enum) +} + +func (enum ListTasksRequestOrderBy) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *ListTasksRequestOrderBy) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = ListTasksRequestOrderBy(ListTasksRequestOrderBy(tmp).String()) + return nil +} + +type RawFormat string + +const ( + RawFormatUnknownRawFormat = RawFormat("unknown_raw_format") + RawFormatBind = RawFormat("bind") +) + +func (enum RawFormat) String() string { + if enum == "" { + // return default value if empty + return "unknown_raw_format" + } + return string(enum) +} + +func (enum RawFormat) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *RawFormat) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = RawFormat(RawFormat(tmp).String()) + return nil +} + +type RecordHTTPServiceConfigStrategy string + +const ( + RecordHTTPServiceConfigStrategyRandom = RecordHTTPServiceConfigStrategy("random") + RecordHTTPServiceConfigStrategyHashed = RecordHTTPServiceConfigStrategy("hashed") + RecordHTTPServiceConfigStrategyAll = RecordHTTPServiceConfigStrategy("all") +) + +func (enum RecordHTTPServiceConfigStrategy) String() string { + if enum == "" { + // return default value if empty + return "random" + } + return string(enum) +} + +func (enum RecordHTTPServiceConfigStrategy) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *RecordHTTPServiceConfigStrategy) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = RecordHTTPServiceConfigStrategy(RecordHTTPServiceConfigStrategy(tmp).String()) + return nil +} + +type RecordType string + +const ( + RecordTypeUnknown = RecordType("unknown") + RecordTypeA = RecordType("A") + RecordTypeAAAA = RecordType("AAAA") + RecordTypeCNAME = RecordType("CNAME") + RecordTypeTXT = RecordType("TXT") + RecordTypeSRV = RecordType("SRV") + RecordTypeTLSA = RecordType("TLSA") + RecordTypeMX = RecordType("MX") + RecordTypeNS = RecordType("NS") + RecordTypePTR = RecordType("PTR") + RecordTypeCAA = RecordType("CAA") + RecordTypeALIAS = RecordType("ALIAS") + RecordTypeLOC = RecordType("LOC") + RecordTypeSSHFP = RecordType("SSHFP") + RecordTypeHINFO = RecordType("HINFO") + RecordTypeRP = RecordType("RP") + RecordTypeURI = RecordType("URI") + RecordTypeDS = RecordType("DS") + RecordTypeNAPTR = RecordType("NAPTR") + RecordTypeDNAME = RecordType("DNAME") +) + +func (enum RecordType) String() string { + if enum == "" { + // return default value if empty + return "unknown" + } + return string(enum) +} + +func (enum RecordType) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *RecordType) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = RecordType(RecordType(tmp).String()) + return nil +} + +type RenewableDomainStatus string + +const ( + RenewableDomainStatusUnknown = RenewableDomainStatus("unknown") + RenewableDomainStatusRenewable = RenewableDomainStatus("renewable") + RenewableDomainStatusLateReneweable = RenewableDomainStatus("late_reneweable") + RenewableDomainStatusNotRenewable = RenewableDomainStatus("not_renewable") +) + +func (enum RenewableDomainStatus) String() string { + if enum == "" { + // return default value if empty + return "unknown" + } + return string(enum) +} + +func (enum RenewableDomainStatus) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *RenewableDomainStatus) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = RenewableDomainStatus(RenewableDomainStatus(tmp).String()) + return nil +} + +type SSLCertificateStatus string + +const ( + SSLCertificateStatusUnknown = SSLCertificateStatus("unknown") + SSLCertificateStatusNew = SSLCertificateStatus("new") + SSLCertificateStatusPending = SSLCertificateStatus("pending") + SSLCertificateStatusSuccess = SSLCertificateStatus("success") + SSLCertificateStatusError = SSLCertificateStatus("error") +) + +func (enum SSLCertificateStatus) String() string { + if enum == "" { + // return default value if empty + return "unknown" + } + return string(enum) +} + +func (enum SSLCertificateStatus) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *SSLCertificateStatus) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = SSLCertificateStatus(SSLCertificateStatus(tmp).String()) + return nil +} + +type TaskStatus string + +const ( + TaskStatusUnavailable = TaskStatus("unavailable") + TaskStatusNew = TaskStatus("new") + TaskStatusWaitingPayment = TaskStatus("waiting_payment") + TaskStatusPending = TaskStatus("pending") + TaskStatusSuccess = TaskStatus("success") + TaskStatusError = TaskStatus("error") +) + +func (enum TaskStatus) String() string { + if enum == "" { + // return default value if empty + return "unavailable" + } + return string(enum) +} + +func (enum TaskStatus) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *TaskStatus) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = TaskStatus(TaskStatus(tmp).String()) + return nil +} + +type TaskType string + +const ( + TaskTypeUnknown = TaskType("unknown") + TaskTypeCreateDomain = TaskType("create_domain") + TaskTypeCreateExternalDomain = TaskType("create_external_domain") + TaskTypeRenewDomain = TaskType("renew_domain") + TaskTypeTransferDomain = TaskType("transfer_domain") + TaskTypeTradeDomain = TaskType("trade_domain") + TaskTypeLockDomainTransfer = TaskType("lock_domain_transfer") + TaskTypeUnlockDomainTransfer = TaskType("unlock_domain_transfer") + TaskTypeEnableDnssec = TaskType("enable_dnssec") + TaskTypeDisableDnssec = TaskType("disable_dnssec") + TaskTypeUpdateDomain = TaskType("update_domain") + TaskTypeUpdateContact = TaskType("update_contact") + TaskTypeDeleteDomain = TaskType("delete_domain") + TaskTypeCancelTask = TaskType("cancel_task") + TaskTypeGenerateSslCertificate = TaskType("generate_ssl_certificate") + TaskTypeRenewSslCertificate = TaskType("renew_ssl_certificate") + TaskTypeSendMessage = TaskType("send_message") + TaskTypeDeleteDomainExpired = TaskType("delete_domain_expired") + TaskTypeDeleteExternalDomain = TaskType("delete_external_domain") + TaskTypeCreateHost = TaskType("create_host") + TaskTypeUpdateHost = TaskType("update_host") + TaskTypeDeleteHost = TaskType("delete_host") +) + +func (enum TaskType) String() string { + if enum == "" { + // return default value if empty + return "unknown" + } + return string(enum) +} + +func (enum TaskType) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf(`"%s"`, enum)), nil +} + +func (enum *TaskType) UnmarshalJSON(data []byte) error { + tmp := "" + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + *enum = TaskType(TaskType(tmp).String()) + return nil +} + +type AvailableDomain struct { + Domain string `json:"domain"` + + Available bool `json:"available"` + + Tld *Tld `json:"tld"` +} + +// CheckContactsCompatibilityResponse: check contacts compatibility response. +type CheckContactsCompatibilityResponse struct { + Compatible bool `json:"compatible"` + + OwnerCheckResult *CheckContactsCompatibilityResponseContactCheckResult `json:"owner_check_result"` + + AdministrativeCheckResult *CheckContactsCompatibilityResponseContactCheckResult `json:"administrative_check_result"` + + TechnicalCheckResult *CheckContactsCompatibilityResponseContactCheckResult `json:"technical_check_result"` +} + +type CheckContactsCompatibilityResponseContactCheckResult struct { + Compatible bool `json:"compatible"` + + ErrorMessage *string `json:"error_message"` +} + +// ClearDNSZoneRecordsResponse: clear dns zone records response. +type ClearDNSZoneRecordsResponse struct { +} + +// Contact: contact. +type Contact struct { + ID string `json:"id"` + // LegalForm: default value: legal_form_unknown + LegalForm ContactLegalForm `json:"legal_form"` + + Firstname string `json:"firstname"` + + Lastname string `json:"lastname"` + + CompanyName string `json:"company_name"` + + Email string `json:"email"` + + EmailAlt string `json:"email_alt"` + + PhoneNumber string `json:"phone_number"` + + FaxNumber string `json:"fax_number"` + + AddressLine1 string `json:"address_line_1"` + + AddressLine2 string `json:"address_line_2"` + + Zip string `json:"zip"` + + City string `json:"city"` + + Country string `json:"country"` + + VatIdentificationCode string `json:"vat_identification_code"` + + CompanyIdentificationCode string `json:"company_identification_code"` + // Lang: default value: unknown_language_code + Lang LanguageCode `json:"lang"` + + Resale bool `json:"resale"` + // Deprecated + Questions *[]*ContactQuestion `json:"questions,omitempty"` + + ExtensionFr *ContactExtensionFR `json:"extension_fr"` + + ExtensionEu *ContactExtensionEU `json:"extension_eu"` + + WhoisOptIn bool `json:"whois_opt_in"` + // EmailStatus: default value: email_status_unknown + EmailStatus ContactEmailStatus `json:"email_status"` + + State string `json:"state"` + + ExtensionNl *ContactExtensionNL `json:"extension_nl"` +} + +type ContactExtensionEU struct { + EuropeanCitizenship string `json:"european_citizenship"` +} + +type ContactExtensionFR struct { + // Mode: default value: mode_unknown + Mode ContactExtensionFRMode `json:"mode"` + + // Precisely one of AssociationInfo, CodeAuthAfnicInfo, DunsInfo, IndividualInfo, TrademarkInfo must be set. + IndividualInfo *ContactExtensionFRIndividualInfo `json:"individual_info,omitempty"` + + // Precisely one of AssociationInfo, CodeAuthAfnicInfo, DunsInfo, IndividualInfo, TrademarkInfo must be set. + DunsInfo *ContactExtensionFRDunsInfo `json:"duns_info,omitempty"` + + // Precisely one of AssociationInfo, CodeAuthAfnicInfo, DunsInfo, IndividualInfo, TrademarkInfo must be set. + AssociationInfo *ContactExtensionFRAssociationInfo `json:"association_info,omitempty"` + + // Precisely one of AssociationInfo, CodeAuthAfnicInfo, DunsInfo, IndividualInfo, TrademarkInfo must be set. + TrademarkInfo *ContactExtensionFRTrademarkInfo `json:"trademark_info,omitempty"` + + // Precisely one of AssociationInfo, CodeAuthAfnicInfo, DunsInfo, IndividualInfo, TrademarkInfo must be set. + CodeAuthAfnicInfo *ContactExtensionFRCodeAuthAfnicInfo `json:"code_auth_afnic_info,omitempty"` +} + +type ContactExtensionFRAssociationInfo struct { + PublicationJo *time.Time `json:"publication_jo"` + + PublicationJoPage uint32 `json:"publication_jo_page"` +} + +type ContactExtensionFRCodeAuthAfnicInfo struct { + CodeAuthAfnic string `json:"code_auth_afnic"` +} + +type ContactExtensionFRDunsInfo struct { + DunsID string `json:"duns_id"` + + LocalID string `json:"local_id"` +} + +type ContactExtensionFRIndividualInfo struct { + WhoisOptIn bool `json:"whois_opt_in"` +} + +type ContactExtensionFRTrademarkInfo struct { + TrademarkInpi string `json:"trademark_inpi"` +} + +type ContactExtensionNL struct { + // LegalForm: default value: legal_form_unknown + LegalForm ContactExtensionNLLegalForm `json:"legal_form"` + + LegalFormRegistrationNumber string `json:"legal_form_registration_number"` +} + +type ContactQuestion struct { + Question string `json:"question"` + + Answer string `json:"answer"` +} + +type ContactRoles struct { + Contact *Contact `json:"contact"` + + Roles map[string]*ContactRolesRoles `json:"roles"` +} + +type ContactRolesRoles struct { + IsOwner bool `json:"is_owner"` + + IsAdministrative bool `json:"is_administrative"` + + IsTechnical bool `json:"is_technical"` +} + +type DNSZone struct { + Domain string `json:"domain"` + + Subdomain string `json:"subdomain"` + + Ns []string `json:"ns"` + + NsDefault []string `json:"ns_default"` + + NsMaster []string `json:"ns_master"` + // Status: default value: unknown + Status DNSZoneStatus `json:"status"` + + Message *string `json:"message"` + + UpdatedAt *time.Time `json:"updated_at"` + + ProjectID string `json:"project_id"` +} + +type DNSZoneVersion struct { + ID string `json:"id"` + + CreatedAt *time.Time `json:"created_at"` +} + +type DSRecord struct { + KeyID uint32 `json:"key_id"` + // Algorithm: default value: rsamd5 + Algorithm DSRecordAlgorithm `json:"algorithm"` + + // Precisely one of Digest, PublicKey must be set. + Digest *DSRecordDigest `json:"digest,omitempty"` + + // Precisely one of Digest, PublicKey must be set. + PublicKey *DSRecordPublicKey `json:"public_key,omitempty"` +} + +type DSRecordDigest struct { + // Type: default value: sha_1 + Type DSRecordDigestType `json:"type"` + + Digest string `json:"digest"` + + PublicKey *DSRecordPublicKey `json:"public_key"` +} + +type DSRecordPublicKey struct { + Key string `json:"key"` +} + +// DeleteDNSZoneResponse: delete dns zone response. +type DeleteDNSZoneResponse struct { +} + +// DeleteExternalDomainResponse: delete external domain response. +type DeleteExternalDomainResponse struct { +} + +// DeleteSSLCertificateResponse: delete ssl certificate response. +type DeleteSSLCertificateResponse struct { +} + +// Domain: domain. +type Domain struct { + Domain string `json:"domain"` + + OrganizationID string `json:"organization_id"` + + ProjectID string `json:"project_id"` + // AutoRenewStatus: default value: feature_status_unknown + AutoRenewStatus DomainFeatureStatus `json:"auto_renew_status"` + + Dnssec *DomainDNSSEC `json:"dnssec"` + + EppCode []string `json:"epp_code"` + + ExpiredAt *time.Time `json:"expired_at"` + + UpdatedAt *time.Time `json:"updated_at"` + + Registrar string `json:"registrar"` + + IsExternal bool `json:"is_external"` + // Status: default value: status_unknown + Status DomainStatus `json:"status"` + + DNSZones []*DNSZone `json:"dns_zones"` + + OwnerContact *Contact `json:"owner_contact"` + + TechnicalContact *Contact `json:"technical_contact"` + + AdministrativeContact *Contact `json:"administrative_contact"` + + // Precisely one of ExternalDomainRegistrationStatus, TransferRegistrationStatus must be set. + ExternalDomainRegistrationStatus *DomainRegistrationStatusExternalDomain `json:"external_domain_registration_status,omitempty"` + + // Precisely one of ExternalDomainRegistrationStatus, TransferRegistrationStatus must be set. + TransferRegistrationStatus *DomainRegistrationStatusTransfer `json:"transfer_registration_status,omitempty"` + + Tld *Tld `json:"tld"` +} + +type DomainDNSSEC struct { + // Status: default value: feature_status_unknown + Status DomainFeatureStatus `json:"status"` + + DsRecords []*DSRecord `json:"ds_records"` +} + +type DomainRegistrationStatusExternalDomain struct { + ValidationToken string `json:"validation_token"` +} + +type DomainRegistrationStatusTransfer struct { + // Status: default value: status_unknown + Status DomainRegistrationStatusTransferStatus `json:"status"` + + VoteCurrentOwner bool `json:"vote_current_owner"` + + VoteNewOwner bool `json:"vote_new_owner"` +} + +type DomainSummary struct { + Domain string `json:"domain"` + + ProjectID string `json:"project_id"` + // AutoRenewStatus: default value: feature_status_unknown + AutoRenewStatus DomainFeatureStatus `json:"auto_renew_status"` + // DnssecStatus: default value: feature_status_unknown + DnssecStatus DomainFeatureStatus `json:"dnssec_status"` + + EppCode []string `json:"epp_code"` + + ExpiredAt *time.Time `json:"expired_at"` + + UpdatedAt *time.Time `json:"updated_at"` + + Registrar string `json:"registrar"` + + IsExternal bool `json:"is_external"` + // Status: default value: status_unknown + Status DomainStatus `json:"status"` + + // Precisely one of ExternalDomainRegistrationStatus, TransferRegistrationStatus must be set. + ExternalDomainRegistrationStatus *DomainRegistrationStatusExternalDomain `json:"external_domain_registration_status,omitempty"` + + // Precisely one of ExternalDomainRegistrationStatus, TransferRegistrationStatus must be set. + TransferRegistrationStatus *DomainRegistrationStatusTransfer `json:"transfer_registration_status,omitempty"` + + OrganizationID string `json:"organization_id"` +} + +// GetDNSZoneTsigKeyResponse: get dns zone tsig key response. +type GetDNSZoneTsigKeyResponse struct { + Name string `json:"name"` + + Key string `json:"key"` + + Algorithm string `json:"algorithm"` +} + +// GetDNSZoneVersionDiffResponse: get dns zone version diff response. +type GetDNSZoneVersionDiffResponse struct { + Changes []*RecordChange `json:"changes"` +} + +// GetDomainAuthCodeResponse: get domain auth code response. +type GetDomainAuthCodeResponse struct { + AuthCode string `json:"auth_code"` +} + +type Host struct { + Domain string `json:"domain"` + + Name string `json:"name"` + + IPs []net.IP `json:"ips"` + // Status: default value: unknown_status + Status HostStatus `json:"status"` +} + +type ImportProviderDNSZoneRequestOnlineV1 struct { + Token string `json:"token"` +} + +// ImportProviderDNSZoneResponse: import provider dns zone response. +type ImportProviderDNSZoneResponse struct { + Records []*Record `json:"records"` +} + +type ImportRawDNSZoneRequestAXFRSource struct { + NameServer string `json:"name_server"` + + TsigKey *ImportRawDNSZoneRequestTsigKey `json:"tsig_key"` +} + +type ImportRawDNSZoneRequestBindSource struct { + Content string `json:"content"` +} + +type ImportRawDNSZoneRequestTsigKey struct { + Name string `json:"name"` + + Key string `json:"key"` + + Algorithm string `json:"algorithm"` +} + +// ImportRawDNSZoneResponse: import raw dns zone response. +type ImportRawDNSZoneResponse struct { + Records []*Record `json:"records"` +} + +// ListContactsResponse: list contacts response. +type ListContactsResponse struct { + TotalCount uint32 `json:"total_count"` + + Contacts []*ContactRoles `json:"contacts"` +} + +// ListDNSZoneNameserversResponse: list dns zone nameservers response. +type ListDNSZoneNameserversResponse struct { + // Ns: DNS zone name servers returned. + Ns []*Nameserver `json:"ns"` +} + +// ListDNSZoneRecordsResponse: list dns zone records response. +type ListDNSZoneRecordsResponse struct { + // TotalCount: total number of DNS zone records. + TotalCount uint32 `json:"total_count"` + // Records: paginated returned DNS zone records. + Records []*Record `json:"records"` +} + +// ListDNSZoneVersionRecordsResponse: list dns zone version records response. +type ListDNSZoneVersionRecordsResponse struct { + // TotalCount: total number of DNS zones versions records. + TotalCount uint32 `json:"total_count"` + + Records []*Record `json:"records"` +} + +// ListDNSZoneVersionsResponse: list dns zone versions response. +type ListDNSZoneVersionsResponse struct { + // TotalCount: total number of DNS zones versions. + TotalCount uint32 `json:"total_count"` + + Versions []*DNSZoneVersion `json:"versions"` +} + +// ListDNSZonesResponse: list dns zones response. +type ListDNSZonesResponse struct { + // TotalCount: total number of DNS zones matching the requested criteria. + TotalCount uint32 `json:"total_count"` + // DNSZones: paginated returned DNS zones. + DNSZones []*DNSZone `json:"dns_zones"` +} + +// ListDomainHostsResponse: list domain hosts response. +type ListDomainHostsResponse struct { + TotalCount uint32 `json:"total_count"` + + Hosts []*Host `json:"hosts"` +} + +// ListDomainsResponse: list domains response. +type ListDomainsResponse struct { + TotalCount uint32 `json:"total_count"` + + Domains []*DomainSummary `json:"domains"` +} + +// ListRenewableDomainsResponse: list renewable domains response. +type ListRenewableDomainsResponse struct { + TotalCount uint32 `json:"total_count"` + + Domains []*RenewableDomain `json:"domains"` +} + +// ListSSLCertificatesResponse: list ssl certificates response. +type ListSSLCertificatesResponse struct { + TotalCount uint32 `json:"total_count"` + + Certificates []*SSLCertificate `json:"certificates"` +} + +// ListTasksResponse: list tasks response. +type ListTasksResponse struct { + TotalCount uint32 `json:"total_count"` + + Tasks []*Task `json:"tasks"` +} + +type Nameserver struct { + Name string `json:"name"` + + IP []string `json:"ip"` +} + +type NewContact struct { + // LegalForm: default value: legal_form_unknown + LegalForm ContactLegalForm `json:"legal_form"` + + Firstname string `json:"firstname"` + + Lastname string `json:"lastname"` + + CompanyName *string `json:"company_name"` + + Email string `json:"email"` + + EmailAlt *string `json:"email_alt"` + + PhoneNumber string `json:"phone_number"` + + FaxNumber *string `json:"fax_number"` + + AddressLine1 string `json:"address_line_1"` + + AddressLine2 *string `json:"address_line_2"` + + Zip string `json:"zip"` + + City string `json:"city"` + + Country string `json:"country"` + + VatIdentificationCode *string `json:"vat_identification_code"` + + CompanyIdentificationCode *string `json:"company_identification_code"` + // Lang: default value: unknown_language_code + Lang LanguageCode `json:"lang"` + + Resale bool `json:"resale"` + // Deprecated + Questions *[]*ContactQuestion `json:"questions,omitempty"` + + ExtensionFr *ContactExtensionFR `json:"extension_fr"` + + ExtensionEu *ContactExtensionEU `json:"extension_eu"` + + WhoisOptIn bool `json:"whois_opt_in"` + + State *string `json:"state"` + + ExtensionNl *ContactExtensionNL `json:"extension_nl"` +} + +type OrderResponse struct { + Domains []string `json:"domains"` + + OrganizationID string `json:"organization_id"` + + ProjectID string `json:"project_id"` + + TaskID string `json:"task_id"` + + CreatedAt *time.Time `json:"created_at"` +} + +type Record struct { + Data string `json:"data"` + + Name string `json:"name"` + + Priority uint32 `json:"priority"` + + TTL uint32 `json:"ttl"` + // Type: default value: unknown + Type RecordType `json:"type"` + + Comment *string `json:"comment"` + + // Precisely one of GeoIPConfig, HTTPServiceConfig, ViewConfig, WeightedConfig must be set. + GeoIPConfig *RecordGeoIPConfig `json:"geo_ip_config,omitempty"` + + // Precisely one of GeoIPConfig, HTTPServiceConfig, ViewConfig, WeightedConfig must be set. + HTTPServiceConfig *RecordHTTPServiceConfig `json:"http_service_config,omitempty"` + + // Precisely one of GeoIPConfig, HTTPServiceConfig, ViewConfig, WeightedConfig must be set. + WeightedConfig *RecordWeightedConfig `json:"weighted_config,omitempty"` + + // Precisely one of GeoIPConfig, HTTPServiceConfig, ViewConfig, WeightedConfig must be set. + ViewConfig *RecordViewConfig `json:"view_config,omitempty"` + + ID string `json:"id"` +} + +type RecordChange struct { + + // Precisely one of Add, Clear, Delete, Set must be set. + Add *RecordChangeAdd `json:"add,omitempty"` + + // Precisely one of Add, Clear, Delete, Set must be set. + Set *RecordChangeSet `json:"set,omitempty"` + + // Precisely one of Add, Clear, Delete, Set must be set. + Delete *RecordChangeDelete `json:"delete,omitempty"` + + // Precisely one of Add, Clear, Delete, Set must be set. + Clear *RecordChangeClear `json:"clear,omitempty"` +} + +type RecordChangeAdd struct { + Records []*Record `json:"records"` +} + +type RecordChangeClear struct { +} + +type RecordChangeDelete struct { + + // Precisely one of ID, IDFields must be set. + ID *string `json:"id,omitempty"` + + // Precisely one of ID, IDFields must be set. + IDFields *RecordIdentifier `json:"id_fields,omitempty"` +} + +type RecordChangeSet struct { + + // Precisely one of ID, IDFields must be set. + ID *string `json:"id,omitempty"` + + // Precisely one of ID, IDFields must be set. + IDFields *RecordIdentifier `json:"id_fields,omitempty"` + + Records []*Record `json:"records"` +} + +type RecordGeoIPConfig struct { + Matches []*RecordGeoIPConfigMatch `json:"matches"` + + Default string `json:"default"` +} + +type RecordGeoIPConfigMatch struct { + Countries []string `json:"countries"` + + Continents []string `json:"continents"` + + Data string `json:"data"` +} + +type RecordHTTPServiceConfig struct { + IPs []net.IP `json:"ips"` + + MustContain *string `json:"must_contain"` + + URL string `json:"url"` + + UserAgent *string `json:"user_agent"` + // Strategy: default value: random + Strategy RecordHTTPServiceConfigStrategy `json:"strategy"` +} + +type RecordIdentifier struct { + Name string `json:"name"` + // Type: default value: unknown + Type RecordType `json:"type"` + + Data *string `json:"data"` + + TTL *uint32 `json:"ttl"` +} + +type RecordViewConfig struct { + Views []*RecordViewConfigView `json:"views"` +} + +type RecordViewConfigView struct { + Subnet string `json:"subnet"` + + Data string `json:"data"` +} + +type RecordWeightedConfig struct { + WeightedIPs []*RecordWeightedConfigWeightedIP `json:"weighted_ips"` +} + +type RecordWeightedConfigWeightedIP struct { + IP net.IP `json:"ip"` + + Weight uint32 `json:"weight"` +} + +// RefreshDNSZoneResponse: refresh dns zone response. +type RefreshDNSZoneResponse struct { + // DNSZones: DNS zones returned. + DNSZones []*DNSZone `json:"dns_zones"` +} + +type RegisterExternalDomainResponse struct { + Domain string `json:"domain"` + + OrganizationID string `json:"organization_id"` + + ValidationToken string `json:"validation_token"` + + CreatedAt *time.Time `json:"created_at"` + + ProjectID string `json:"project_id"` +} + +type RenewableDomain struct { + Domain string `json:"domain"` + + ProjectID string `json:"project_id"` + + OrganizationID string `json:"organization_id"` + // Status: default value: unknown + Status RenewableDomainStatus `json:"status"` + + RenewableDurationInYears *int32 `json:"renewable_duration_in_years"` + + ExpiredAt *time.Time `json:"expired_at"` + + LimitRenewAt *time.Time `json:"limit_renew_at"` + + LimitRedemptionAt *time.Time `json:"limit_redemption_at"` + + EstimatedDeleteAt *time.Time `json:"estimated_delete_at"` + + Tld *Tld `json:"tld"` +} + +// RestoreDNSZoneVersionResponse: restore dns zone version response. +type RestoreDNSZoneVersionResponse struct { +} + +type SSLCertificate struct { + DNSZone string `json:"dns_zone"` + + AlternativeDNSZones []string `json:"alternative_dns_zones"` + // Status: default value: unknown + Status SSLCertificateStatus `json:"status"` + + PrivateKey string `json:"private_key"` + + CertificateChain string `json:"certificate_chain"` + + CreatedAt *time.Time `json:"created_at"` + + ExpiredAt *time.Time `json:"expired_at"` +} + +// SearchAvailableDomainsResponse: search available domains response. +type SearchAvailableDomainsResponse struct { + // AvailableDomains: array of available domains. + AvailableDomains []*AvailableDomain `json:"available_domains"` +} + +type Task struct { + ID string `json:"id"` + + ProjectID string `json:"project_id"` + + OrganizationID string `json:"organization_id"` + + Domain *string `json:"domain"` + // Type: default value: unknown + Type TaskType `json:"type"` + // Status: default value: unavailable + Status TaskStatus `json:"status"` + + StartedAt *time.Time `json:"started_at"` + + UpdatedAt *time.Time `json:"updated_at"` + + Message *string `json:"message"` +} + +type Tld struct { + Name string `json:"name"` + + DnssecSupport bool `json:"dnssec_support"` + + DurationInYearsMin uint32 `json:"duration_in_years_min"` + + DurationInYearsMax uint32 `json:"duration_in_years_max"` + + IdnSupport bool `json:"idn_support"` + + Offers map[string]*TldOffer `json:"offers"` + + Specifications map[string]string `json:"specifications"` +} + +type TldOffer struct { + Action string `json:"action"` + + OperationPath string `json:"operation_path"` + + Price *scw.Money `json:"price"` +} + +type TransferInDomainRequestTransferRequest struct { + Domain string `json:"domain"` + + AuthCode string `json:"auth_code"` +} + +type UpdateContactRequestQuestion struct { + Question *string `json:"question"` + + Answer *string `json:"answer"` +} + +// UpdateDNSZoneNameserversResponse: update dns zone nameservers response. +type UpdateDNSZoneNameserversResponse struct { + // Ns: DNS zone name servers returned. + Ns []*Nameserver `json:"ns"` +} + +// UpdateDNSZoneRecordsResponse: update dns zone records response. +type UpdateDNSZoneRecordsResponse struct { + // Records: DNS zone records returned. + Records []*Record `json:"records"` +} + +// Service API + +type ListDNSZonesRequest struct { + // OrganizationID: organization ID on which to filter the returned DNS zones. + OrganizationID *string `json:"-"` + // ProjectID: project ID on which to filter the returned DNS zones. + ProjectID *string `json:"-"` + // OrderBy: sort order of the returned DNS zones. + // Default value: domain_asc + OrderBy ListDNSZonesRequestOrderBy `json:"-"` + // Page: page number to return, from the paginated results. + Page *int32 `json:"-"` + // PageSize: maximum number of DNS zones to return per page. + PageSize *uint32 `json:"-"` + // Domain: domain on which to filter the returned DNS zones. + Domain string `json:"-"` + // DNSZone: DNS zone on which to filter the returned DNS zones. + DNSZone string `json:"-"` +} + +// ListDNSZones: list DNS zones. +// Retrieve the list of DNS zones you can manage and filter DNS zones associated with specific domain names. +func (s *API) ListDNSZones(req *ListDNSZonesRequest, opts ...scw.RequestOption) (*ListDNSZonesResponse, error) { + var err error + + defaultPageSize, exist := s.client.GetDefaultPageSize() + if (req.PageSize == nil || *req.PageSize == 0) && exist { + req.PageSize = &defaultPageSize + } + + query := url.Values{} + parameter.AddToQuery(query, "organization_id", req.OrganizationID) + parameter.AddToQuery(query, "project_id", req.ProjectID) + parameter.AddToQuery(query, "order_by", req.OrderBy) + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "page_size", req.PageSize) + parameter.AddToQuery(query, "domain", req.Domain) + parameter.AddToQuery(query, "dns_zone", req.DNSZone) + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/dns-zones", + Query: query, + Headers: http.Header{}, + } + + var resp ListDNSZonesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type CreateDNSZoneRequest struct { + // Domain: domain in which to crreate the DNS zone. + Domain string `json:"domain"` + // Subdomain: subdomain of the DNS zone to create. + Subdomain string `json:"subdomain"` + // ProjectID: project ID in which to create the DNS zone. + ProjectID string `json:"project_id"` +} + +// CreateDNSZone: create a DNS zone. +// Create a new DNS zone specified by the domain name, the subdomain and the Project ID. +func (s *API) CreateDNSZone(req *CreateDNSZoneRequest, opts ...scw.RequestOption) (*DNSZone, error) { + var err error + + if req.ProjectID == "" { + defaultProjectID, _ := s.client.GetDefaultProjectID() + req.ProjectID = defaultProjectID + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/dns-zones", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp DNSZone + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type UpdateDNSZoneRequest struct { + // DNSZone: DNS zone to update. + DNSZone string `json:"-"` + // NewDNSZone: name of the new DNS zone to create. + NewDNSZone *string `json:"new_dns_zone"` + // ProjectID: project ID in which to create the new DNS zone. + ProjectID string `json:"project_id"` +} + +// UpdateDNSZone: update a DNS zone. +// Update the name and/or the Organizations for a DNS zone. +func (s *API) UpdateDNSZone(req *UpdateDNSZoneRequest, opts ...scw.RequestOption) (*DNSZone, error) { + var err error + + if req.ProjectID == "" { + defaultProjectID, _ := s.client.GetDefaultProjectID() + req.ProjectID = defaultProjectID + } + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/domain/v2beta1/dns-zones/" + fmt.Sprint(req.DNSZone) + "", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp DNSZone + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type CloneDNSZoneRequest struct { + // DNSZone: DNS zone to clone. + DNSZone string `json:"-"` + // DestDNSZone: destination DNS zone in which to clone the chosen DNS zone. + DestDNSZone string `json:"dest_dns_zone"` + // Overwrite: specifies whether or not the destination DNS zone will be overwritten. + Overwrite bool `json:"overwrite"` + // ProjectID: project ID of the destination DNS zone. + ProjectID *string `json:"project_id"` +} + +// CloneDNSZone: clone a DNS zone. +// Clone an existing DNS zone with all its records into a new DNS zone. +func (s *API) CloneDNSZone(req *CloneDNSZoneRequest, opts ...scw.RequestOption) (*DNSZone, error) { + var err error + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/dns-zones/" + fmt.Sprint(req.DNSZone) + "/clone", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp DNSZone + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type DeleteDNSZoneRequest struct { + // DNSZone: DNS zone to delete. + DNSZone string `json:"-"` + // ProjectID: project ID of the DNS zone to delete. + ProjectID string `json:"-"` +} + +// DeleteDNSZone: delete a DNS zone. +// Delete a DNS zone and all its records. +func (s *API) DeleteDNSZone(req *DeleteDNSZoneRequest, opts ...scw.RequestOption) (*DeleteDNSZoneResponse, error) { + var err error + + if req.ProjectID == "" { + defaultProjectID, _ := s.client.GetDefaultProjectID() + req.ProjectID = defaultProjectID + } + + query := url.Values{} + parameter.AddToQuery(query, "project_id", req.ProjectID) + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/domain/v2beta1/dns-zones/" + fmt.Sprint(req.DNSZone) + "", + Query: query, + Headers: http.Header{}, + } + + var resp DeleteDNSZoneResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ListDNSZoneRecordsRequest struct { + // DNSZone: DNS zone on which to filter the returned DNS zone records. + DNSZone string `json:"-"` + // ProjectID: project ID on which to filter the returned DNS zone records. + ProjectID *string `json:"-"` + // OrderBy: sort order of the returned DNS zone records. + // Default value: name_asc + OrderBy ListDNSZoneRecordsRequestOrderBy `json:"-"` + // Page: page number to return, from the paginated results. + Page *int32 `json:"-"` + // PageSize: maximum number of DNS zone records per page. + PageSize *uint32 `json:"-"` + // Name: name on which to filter the returned DNS zone records. + Name string `json:"-"` + // Type: record type on which to filter the returned DNS zone records. + // Default value: unknown + Type RecordType `json:"-"` + // ID: record ID on which to filter the returned DNS zone records. + ID *string `json:"-"` +} + +// ListDNSZoneRecords: list records within a DNS zone. +// Retrieve a list of DNS records within a DNS zone that has default name servers. +// You can filter records by type and name. +func (s *API) ListDNSZoneRecords(req *ListDNSZoneRecordsRequest, opts ...scw.RequestOption) (*ListDNSZoneRecordsResponse, error) { + var err error + + defaultPageSize, exist := s.client.GetDefaultPageSize() + if (req.PageSize == nil || *req.PageSize == 0) && exist { + req.PageSize = &defaultPageSize + } + + query := url.Values{} + parameter.AddToQuery(query, "project_id", req.ProjectID) + parameter.AddToQuery(query, "order_by", req.OrderBy) + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "page_size", req.PageSize) + parameter.AddToQuery(query, "name", req.Name) + parameter.AddToQuery(query, "type", req.Type) + parameter.AddToQuery(query, "id", req.ID) + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/dns-zones/" + fmt.Sprint(req.DNSZone) + "/records", + Query: query, + Headers: http.Header{}, + } + + var resp ListDNSZoneRecordsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type UpdateDNSZoneRecordsRequest struct { + // DNSZone: DNS zone in which to update the DNS zone records. + DNSZone string `json:"-"` + // Changes: changes made to the records. + Changes []*RecordChange `json:"changes"` + // ReturnAllRecords: specifies whether or not to return all the records. + ReturnAllRecords *bool `json:"return_all_records"` + // DisallowNewZoneCreation: disable the creation of the target zone if it does not exist. Target zone creation is disabled by default. + DisallowNewZoneCreation bool `json:"disallow_new_zone_creation"` + // Serial: use the provided serial (0) instead of the auto-increment serial. + Serial *uint64 `json:"serial"` +} + +// UpdateDNSZoneRecords: update records within a DNS zone. +// Update records within a DNS zone that has default name servers and perform several actions on your records. +// +// Actions include: +// - add: allows you to add a new record or add a new IP to an existing A record, for example +// - set: allows you to edit a record or edit an IP from an existing A record, for example +// - delete: allows you to delete a record or delete an IP from an existing A record, for example +// - clear: allows you to delete all records from a DNS zone +// +// All edits will be versioned. +func (s *API) UpdateDNSZoneRecords(req *UpdateDNSZoneRecordsRequest, opts ...scw.RequestOption) (*UpdateDNSZoneRecordsResponse, error) { + var err error + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/domain/v2beta1/dns-zones/" + fmt.Sprint(req.DNSZone) + "/records", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp UpdateDNSZoneRecordsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ListDNSZoneNameserversRequest struct { + // DNSZone: DNS zone on which to filter the returned DNS zone name servers. + DNSZone string `json:"-"` + // ProjectID: project ID on which to filter the returned DNS zone name servers. + ProjectID *string `json:"-"` +} + +// ListDNSZoneNameservers: list name servers within a DNS zone. +// Retrieve a list of name servers within a DNS zone and their optional glue records. +func (s *API) ListDNSZoneNameservers(req *ListDNSZoneNameserversRequest, opts ...scw.RequestOption) (*ListDNSZoneNameserversResponse, error) { + var err error + + query := url.Values{} + parameter.AddToQuery(query, "project_id", req.ProjectID) + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/dns-zones/" + fmt.Sprint(req.DNSZone) + "/nameservers", + Query: query, + Headers: http.Header{}, + } + + var resp ListDNSZoneNameserversResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type UpdateDNSZoneNameserversRequest struct { + // DNSZone: DNS zone in which to update the DNS zone name servers. + DNSZone string `json:"-"` + // Ns: new DNS zone name servers. + Ns []*Nameserver `json:"ns"` +} + +// UpdateDNSZoneNameservers: update name servers within a DNS zone. +// Update name servers within a DNS zone and set optional glue records. +func (s *API) UpdateDNSZoneNameservers(req *UpdateDNSZoneNameserversRequest, opts ...scw.RequestOption) (*UpdateDNSZoneNameserversResponse, error) { + var err error + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PUT", + Path: "/domain/v2beta1/dns-zones/" + fmt.Sprint(req.DNSZone) + "/nameservers", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp UpdateDNSZoneNameserversResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ClearDNSZoneRecordsRequest struct { + // DNSZone: DNS zone to clear. + DNSZone string `json:"-"` +} + +// ClearDNSZoneRecords: clear records within a DNS zone. +// Delete all records within a DNS zone that has default name servers.
+// All edits will be versioned. +func (s *API) ClearDNSZoneRecords(req *ClearDNSZoneRecordsRequest, opts ...scw.RequestOption) (*ClearDNSZoneRecordsResponse, error) { + var err error + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/domain/v2beta1/dns-zones/" + fmt.Sprint(req.DNSZone) + "/records", + Headers: http.Header{}, + } + + var resp ClearDNSZoneRecordsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ExportRawDNSZoneRequest struct { + // DNSZone: DNS zone to export. + DNSZone string `json:"-"` + // Format: DNS zone format. + // Default value: bind + Format RawFormat `json:"-"` +} + +// ExportRawDNSZone: export a raw DNS zone. +// Export a DNS zone with default name servers, in a specific format. +func (s *API) ExportRawDNSZone(req *ExportRawDNSZoneRequest, opts ...scw.RequestOption) (*scw.File, error) { + var err error + + query := url.Values{} + parameter.AddToQuery(query, "format", req.Format) + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/dns-zones/" + fmt.Sprint(req.DNSZone) + "/raw", + Query: query, + Headers: http.Header{}, + } + + var resp scw.File + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ImportRawDNSZoneRequest struct { + // DNSZone: DNS zone to import. + DNSZone string `json:"-"` + // Deprecated + Content *string `json:"content,omitempty"` + + ProjectID string `json:"project_id"` + // Deprecated: Format: default value: unknown_raw_format + Format *RawFormat `json:"format,omitempty"` + // BindSource: import a bind file format. + // Precisely one of AxfrSource, BindSource must be set. + BindSource *ImportRawDNSZoneRequestBindSource `json:"bind_source,omitempty"` + // AxfrSource: import from the name server given with TSIG, to use or not. + // Precisely one of AxfrSource, BindSource must be set. + AxfrSource *ImportRawDNSZoneRequestAXFRSource `json:"axfr_source,omitempty"` +} + +// ImportRawDNSZone: import a raw DNS zone. +// Import and replace the format of records from a given provider, with default name servers. +func (s *API) ImportRawDNSZone(req *ImportRawDNSZoneRequest, opts ...scw.RequestOption) (*ImportRawDNSZoneResponse, error) { + var err error + + if req.ProjectID == "" { + defaultProjectID, _ := s.client.GetDefaultProjectID() + req.ProjectID = defaultProjectID + } + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/dns-zones/" + fmt.Sprint(req.DNSZone) + "/raw", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp ImportRawDNSZoneResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ImportProviderDNSZoneRequest struct { + DNSZone string `json:"-"` + + // Precisely one of OnlineV1 must be set. + OnlineV1 *ImportProviderDNSZoneRequestOnlineV1 `json:"online_v1,omitempty"` +} + +// ImportProviderDNSZone: import a DNS zone from another provider. +// Import and replace the format of records from a given provider, with default name servers. +func (s *API) ImportProviderDNSZone(req *ImportProviderDNSZoneRequest, opts ...scw.RequestOption) (*ImportProviderDNSZoneResponse, error) { + var err error + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/dns-zones/" + fmt.Sprint(req.DNSZone) + "/import-provider", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp ImportProviderDNSZoneResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RefreshDNSZoneRequest struct { + // DNSZone: DNS zone to refresh. + DNSZone string `json:"-"` + // RecreateDNSZone: specifies whether or not to recreate the DNS zone. + RecreateDNSZone bool `json:"recreate_dns_zone"` + // RecreateSubDNSZone: specifies whether or not to recreate the sub DNS zone. + RecreateSubDNSZone bool `json:"recreate_sub_dns_zone"` +} + +// RefreshDNSZone: refresh a DNS zone. +// Refresh an SOA DNS zone to reload the records in the DNS zone and update the SOA serial. +// You can recreate the given DNS zone and its sub DNS zone if needed. +func (s *API) RefreshDNSZone(req *RefreshDNSZoneRequest, opts ...scw.RequestOption) (*RefreshDNSZoneResponse, error) { + var err error + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/dns-zones/" + fmt.Sprint(req.DNSZone) + "/refresh", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp RefreshDNSZoneResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ListDNSZoneVersionsRequest struct { + DNSZone string `json:"-"` + // Page: page number to return, from the paginated results. + Page *int32 `json:"-"` + // PageSize: maximum number of DNS zones versions per page. + PageSize *uint32 `json:"-"` +} + +// ListDNSZoneVersions: list versions of a DNS zone. +// Retrieve a list of a DNS zone's versions.
+// The maximum version count is 100. If the count reaches this limit, the oldest version will be deleted after each new modification. +func (s *API) ListDNSZoneVersions(req *ListDNSZoneVersionsRequest, opts ...scw.RequestOption) (*ListDNSZoneVersionsResponse, error) { + var err error + + defaultPageSize, exist := s.client.GetDefaultPageSize() + if (req.PageSize == nil || *req.PageSize == 0) && exist { + req.PageSize = &defaultPageSize + } + + query := url.Values{} + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "page_size", req.PageSize) + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/dns-zones/" + fmt.Sprint(req.DNSZone) + "/versions", + Query: query, + Headers: http.Header{}, + } + + var resp ListDNSZoneVersionsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ListDNSZoneVersionRecordsRequest struct { + DNSZoneVersionID string `json:"-"` + // Page: page number to return, from the paginated results. + Page *int32 `json:"-"` + // PageSize: maximum number of DNS zones versions records per page. + PageSize *uint32 `json:"-"` +} + +// ListDNSZoneVersionRecords: list records from a given version of a specific DNS zone. +// Retrieve a list of records from a specific DNS zone version. +func (s *API) ListDNSZoneVersionRecords(req *ListDNSZoneVersionRecordsRequest, opts ...scw.RequestOption) (*ListDNSZoneVersionRecordsResponse, error) { + var err error + + defaultPageSize, exist := s.client.GetDefaultPageSize() + if (req.PageSize == nil || *req.PageSize == 0) && exist { + req.PageSize = &defaultPageSize + } + + query := url.Values{} + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "page_size", req.PageSize) + + if fmt.Sprint(req.DNSZoneVersionID) == "" { + return nil, errors.New("field DNSZoneVersionID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/dns-zones/version/" + fmt.Sprint(req.DNSZoneVersionID) + "", + Query: query, + Headers: http.Header{}, + } + + var resp ListDNSZoneVersionRecordsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type GetDNSZoneVersionDiffRequest struct { + DNSZoneVersionID string `json:"-"` +} + +// GetDNSZoneVersionDiff: access differences from a specific DNS zone version. +// Access a previous DNS zone version to see the differences from another specific version. +func (s *API) GetDNSZoneVersionDiff(req *GetDNSZoneVersionDiffRequest, opts ...scw.RequestOption) (*GetDNSZoneVersionDiffResponse, error) { + var err error + + if fmt.Sprint(req.DNSZoneVersionID) == "" { + return nil, errors.New("field DNSZoneVersionID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/dns-zones/version/" + fmt.Sprint(req.DNSZoneVersionID) + "/diff", + Headers: http.Header{}, + } + + var resp GetDNSZoneVersionDiffResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RestoreDNSZoneVersionRequest struct { + DNSZoneVersionID string `json:"-"` +} + +// RestoreDNSZoneVersion: restore a DNS zone version. +// Restore and activate a version of a specific DNS zone. +func (s *API) RestoreDNSZoneVersion(req *RestoreDNSZoneVersionRequest, opts ...scw.RequestOption) (*RestoreDNSZoneVersionResponse, error) { + var err error + + if fmt.Sprint(req.DNSZoneVersionID) == "" { + return nil, errors.New("field DNSZoneVersionID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/dns-zones/version/" + fmt.Sprint(req.DNSZoneVersionID) + "/restore", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp RestoreDNSZoneVersionResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type GetSSLCertificateRequest struct { + DNSZone string `json:"-"` +} + +// GetSSLCertificate: get a DNS zone's TLS certificate. +// Get the DNS zone's TLS certificate. If you do not have a certificate, the ouptut returns `no certificate found`. +func (s *API) GetSSLCertificate(req *GetSSLCertificateRequest, opts ...scw.RequestOption) (*SSLCertificate, error) { + var err error + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/ssl-certificates/" + fmt.Sprint(req.DNSZone) + "", + Headers: http.Header{}, + } + + var resp SSLCertificate + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type CreateSSLCertificateRequest struct { + DNSZone string `json:"dns_zone"` + + AlternativeDNSZones []string `json:"alternative_dns_zones"` +} + +// CreateSSLCertificate: create or get the DNS zone's TLS certificate. +// Create a new TLS certificate or retrieve information about an existing TLS certificate. +func (s *API) CreateSSLCertificate(req *CreateSSLCertificateRequest, opts ...scw.RequestOption) (*SSLCertificate, error) { + var err error + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/ssl-certificates", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp SSLCertificate + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type ListSSLCertificatesRequest struct { + DNSZone string `json:"-"` + + Page *int32 `json:"-"` + + PageSize *uint32 `json:"-"` + + ProjectID *string `json:"-"` +} + +// ListSSLCertificates: list a user's TLS certificates. +// List all the TLS certificates a user has created, specified by the user's Project ID and the DNS zone. +func (s *API) ListSSLCertificates(req *ListSSLCertificatesRequest, opts ...scw.RequestOption) (*ListSSLCertificatesResponse, error) { + var err error + + defaultPageSize, exist := s.client.GetDefaultPageSize() + if (req.PageSize == nil || *req.PageSize == 0) && exist { + req.PageSize = &defaultPageSize + } + + query := url.Values{} + parameter.AddToQuery(query, "dns_zone", req.DNSZone) + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "page_size", req.PageSize) + parameter.AddToQuery(query, "project_id", req.ProjectID) + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/ssl-certificates", + Query: query, + Headers: http.Header{}, + } + + var resp ListSSLCertificatesResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type DeleteSSLCertificateRequest struct { + DNSZone string `json:"-"` +} + +// DeleteSSLCertificate: delete a TLS certificate. +// Delete an existing TLS certificate specified by its DNS zone. Deleting a TLS certificate is permanent and cannot be undone. +func (s *API) DeleteSSLCertificate(req *DeleteSSLCertificateRequest, opts ...scw.RequestOption) (*DeleteSSLCertificateResponse, error) { + var err error + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/domain/v2beta1/ssl-certificates/" + fmt.Sprint(req.DNSZone) + "", + Headers: http.Header{}, + } + + var resp DeleteSSLCertificateResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type GetDNSZoneTsigKeyRequest struct { + DNSZone string `json:"-"` +} + +// GetDNSZoneTsigKey: get the DNS zone's TSIG key. +// Retrieve information about the TSIG key of a given DNS zone to allow AXFR requests. +func (s *API) GetDNSZoneTsigKey(req *GetDNSZoneTsigKeyRequest, opts ...scw.RequestOption) (*GetDNSZoneTsigKeyResponse, error) { + var err error + + if fmt.Sprint(req.DNSZone) == "" { + return nil, errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/dns-zones/" + fmt.Sprint(req.DNSZone) + "/tsig-key", + Headers: http.Header{}, + } + + var resp GetDNSZoneTsigKeyResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type DeleteDNSZoneTsigKeyRequest struct { + DNSZone string `json:"-"` +} + +// DeleteDNSZoneTsigKey: delete the DNS zone's TSIG key. +// Delete an existing TSIG key specified by its DNS zone. Deleting a TSIG key is permanent and cannot be undone. +func (s *API) DeleteDNSZoneTsigKey(req *DeleteDNSZoneTsigKeyRequest, opts ...scw.RequestOption) error { + var err error + + if fmt.Sprint(req.DNSZone) == "" { + return errors.New("field DNSZone cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/domain/v2beta1/dns-zones/" + fmt.Sprint(req.DNSZone) + "/tsig-key", + Headers: http.Header{}, + } + + err = s.client.Do(scwReq, nil, opts...) + if err != nil { + return err + } + return nil +} + +// Service RegistrarAPI + +type RegistrarAPIListTasksRequest struct { + Page *int32 `json:"-"` + + PageSize *uint32 `json:"-"` + + ProjectID *string `json:"-"` + + OrganizationID *string `json:"-"` + + Domain *string `json:"-"` + + Types []TaskType `json:"-"` + + Statuses []TaskStatus `json:"-"` + // OrderBy: default value: domain_desc + OrderBy ListTasksRequestOrderBy `json:"-"` +} + +// ListTasks: list tasks. +// List all account tasks. +// You can filter the list by domain name. +func (s *RegistrarAPI) ListTasks(req *RegistrarAPIListTasksRequest, opts ...scw.RequestOption) (*ListTasksResponse, error) { + var err error + + defaultPageSize, exist := s.client.GetDefaultPageSize() + if (req.PageSize == nil || *req.PageSize == 0) && exist { + req.PageSize = &defaultPageSize + } + + query := url.Values{} + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "page_size", req.PageSize) + parameter.AddToQuery(query, "project_id", req.ProjectID) + parameter.AddToQuery(query, "organization_id", req.OrganizationID) + parameter.AddToQuery(query, "domain", req.Domain) + parameter.AddToQuery(query, "types", req.Types) + parameter.AddToQuery(query, "statuses", req.Statuses) + parameter.AddToQuery(query, "order_by", req.OrderBy) + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/tasks", + Query: query, + Headers: http.Header{}, + } + + var resp ListTasksResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIBuyDomainsRequest struct { + Domains []string `json:"domains"` + + DurationInYears uint32 `json:"duration_in_years"` + + ProjectID string `json:"project_id"` + + // Precisely one of OwnerContact, OwnerContactID must be set. + OwnerContactID *string `json:"owner_contact_id,omitempty"` + + // Precisely one of OwnerContact, OwnerContactID must be set. + OwnerContact *NewContact `json:"owner_contact,omitempty"` + + // Precisely one of AdministrativeContact, AdministrativeContactID must be set. + AdministrativeContactID *string `json:"administrative_contact_id,omitempty"` + + // Precisely one of AdministrativeContact, AdministrativeContactID must be set. + AdministrativeContact *NewContact `json:"administrative_contact,omitempty"` + + // Precisely one of TechnicalContact, TechnicalContactID must be set. + TechnicalContactID *string `json:"technical_contact_id,omitempty"` + + // Precisely one of TechnicalContact, TechnicalContactID must be set. + TechnicalContact *NewContact `json:"technical_contact,omitempty"` +} + +// BuyDomains: buy one or more domains. +// Request the registration of domain names. +// You can provide an already existing domain's contact or a new contact. +func (s *RegistrarAPI) BuyDomains(req *RegistrarAPIBuyDomainsRequest, opts ...scw.RequestOption) (*OrderResponse, error) { + var err error + + if req.ProjectID == "" { + defaultProjectID, _ := s.client.GetDefaultProjectID() + req.ProjectID = defaultProjectID + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/buy-domains", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp OrderResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIRenewDomainsRequest struct { + Domains []string `json:"domains"` + + DurationInYears uint32 `json:"duration_in_years"` + + ForceLateRenewal *bool `json:"force_late_renewal"` +} + +// RenewDomains: renew one or more domains. +// Request the renewal of domain names. +func (s *RegistrarAPI) RenewDomains(req *RegistrarAPIRenewDomainsRequest, opts ...scw.RequestOption) (*OrderResponse, error) { + var err error + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/renew-domains", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp OrderResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPITransferInDomainRequest struct { + Domains []*TransferInDomainRequestTransferRequest `json:"domains"` + + ProjectID string `json:"project_id"` + + // Precisely one of OwnerContact, OwnerContactID must be set. + OwnerContactID *string `json:"owner_contact_id,omitempty"` + + // Precisely one of OwnerContact, OwnerContactID must be set. + OwnerContact *NewContact `json:"owner_contact,omitempty"` + + // Precisely one of AdministrativeContact, AdministrativeContactID must be set. + AdministrativeContactID *string `json:"administrative_contact_id,omitempty"` + + // Precisely one of AdministrativeContact, AdministrativeContactID must be set. + AdministrativeContact *NewContact `json:"administrative_contact,omitempty"` + + // Precisely one of TechnicalContact, TechnicalContactID must be set. + TechnicalContactID *string `json:"technical_contact_id,omitempty"` + + // Precisely one of TechnicalContact, TechnicalContactID must be set. + TechnicalContact *NewContact `json:"technical_contact,omitempty"` +} + +// TransferInDomain: transfer a domain. +// Request the transfer from another registrar domain to Scaleway. +func (s *RegistrarAPI) TransferInDomain(req *RegistrarAPITransferInDomainRequest, opts ...scw.RequestOption) (*OrderResponse, error) { + var err error + + if req.ProjectID == "" { + defaultProjectID, _ := s.client.GetDefaultProjectID() + req.ProjectID = defaultProjectID + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/domains/transfer-domains", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp OrderResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPITradeDomainRequest struct { + Domain string `json:"-"` + + ProjectID *string `json:"project_id"` + + // Precisely one of NewOwnerContact, NewOwnerContactID must be set. + NewOwnerContactID *string `json:"new_owner_contact_id,omitempty"` + + // Precisely one of NewOwnerContact, NewOwnerContactID must be set. + NewOwnerContact *NewContact `json:"new_owner_contact,omitempty"` +} + +// TradeDomain: trade a domain contact. +// Request a trade for the contact owner.
+// If an `organization_id` is given, the change is from the current Scaleway account to another Scaleway account.
+// If no contact is given, the first contact of the other Scaleway account is taken.
+// If the other Scaleway account has no contact. An error occurs. +func (s *RegistrarAPI) TradeDomain(req *RegistrarAPITradeDomainRequest, opts ...scw.RequestOption) (*OrderResponse, error) { + var err error + + if fmt.Sprint(req.Domain) == "" { + return nil, errors.New("field Domain cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/domains/" + fmt.Sprint(req.Domain) + "/trade", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp OrderResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIRegisterExternalDomainRequest struct { + Domain string `json:"domain"` + + ProjectID string `json:"project_id"` +} + +// RegisterExternalDomain: register an external domain. +// Request the registration of an external domain name. +func (s *RegistrarAPI) RegisterExternalDomain(req *RegistrarAPIRegisterExternalDomainRequest, opts ...scw.RequestOption) (*RegisterExternalDomainResponse, error) { + var err error + + if req.ProjectID == "" { + defaultProjectID, _ := s.client.GetDefaultProjectID() + req.ProjectID = defaultProjectID + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/external-domains", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp RegisterExternalDomainResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIDeleteExternalDomainRequest struct { + Domain string `json:"-"` +} + +// DeleteExternalDomain: delete an external domain. +// Delete an external domain name. +func (s *RegistrarAPI) DeleteExternalDomain(req *RegistrarAPIDeleteExternalDomainRequest, opts ...scw.RequestOption) (*DeleteExternalDomainResponse, error) { + var err error + + if fmt.Sprint(req.Domain) == "" { + return nil, errors.New("field Domain cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/domain/v2beta1/external-domains/" + fmt.Sprint(req.Domain) + "", + Headers: http.Header{}, + } + + var resp DeleteExternalDomainResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPICheckContactsCompatibilityRequest struct { + Domains []string `json:"domains"` + + Tlds []string `json:"tlds"` + + // Precisely one of OwnerContact, OwnerContactID must be set. + OwnerContactID *string `json:"owner_contact_id,omitempty"` + + // Precisely one of OwnerContact, OwnerContactID must be set. + OwnerContact *NewContact `json:"owner_contact,omitempty"` + + // Precisely one of AdministrativeContact, AdministrativeContactID must be set. + AdministrativeContactID *string `json:"administrative_contact_id,omitempty"` + + // Precisely one of AdministrativeContact, AdministrativeContactID must be set. + AdministrativeContact *NewContact `json:"administrative_contact,omitempty"` + + // Precisely one of TechnicalContact, TechnicalContactID must be set. + TechnicalContactID *string `json:"technical_contact_id,omitempty"` + + // Precisely one of TechnicalContact, TechnicalContactID must be set. + TechnicalContact *NewContact `json:"technical_contact,omitempty"` +} + +// CheckContactsCompatibility: check if contacts are compatible against a domain or a tld. +// Check if contacts are compatible against a domain or a tld. +// If not, it will return the information requiring a correction. +func (s *RegistrarAPI) CheckContactsCompatibility(req *RegistrarAPICheckContactsCompatibilityRequest, opts ...scw.RequestOption) (*CheckContactsCompatibilityResponse, error) { + var err error + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/check-contacts-compatibility", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp CheckContactsCompatibilityResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIListContactsRequest struct { + Page *int32 `json:"-"` + + PageSize *uint32 `json:"-"` + + Domain *string `json:"-"` + + ProjectID *string `json:"-"` + + OrganizationID *string `json:"-"` +} + +// ListContacts: list contacts. +// Return a list of contacts with their domains and roles. +// You can filter the list by domain name. +func (s *RegistrarAPI) ListContacts(req *RegistrarAPIListContactsRequest, opts ...scw.RequestOption) (*ListContactsResponse, error) { + var err error + + defaultPageSize, exist := s.client.GetDefaultPageSize() + if (req.PageSize == nil || *req.PageSize == 0) && exist { + req.PageSize = &defaultPageSize + } + + query := url.Values{} + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "page_size", req.PageSize) + parameter.AddToQuery(query, "domain", req.Domain) + parameter.AddToQuery(query, "project_id", req.ProjectID) + parameter.AddToQuery(query, "organization_id", req.OrganizationID) + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/contacts", + Query: query, + Headers: http.Header{}, + } + + var resp ListContactsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIGetContactRequest struct { + ContactID string `json:"-"` +} + +// GetContact: get a contact. +// Return a contact details retrieved from the registrar using a given contact ID. +func (s *RegistrarAPI) GetContact(req *RegistrarAPIGetContactRequest, opts ...scw.RequestOption) (*Contact, error) { + var err error + + if fmt.Sprint(req.ContactID) == "" { + return nil, errors.New("field ContactID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/contacts/" + fmt.Sprint(req.ContactID) + "", + Headers: http.Header{}, + } + + var resp Contact + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIUpdateContactRequest struct { + ContactID string `json:"-"` + + Email *string `json:"email"` + + EmailAlt *string `json:"email_alt"` + + PhoneNumber *string `json:"phone_number"` + + FaxNumber *string `json:"fax_number"` + + AddressLine1 *string `json:"address_line_1"` + + AddressLine2 *string `json:"address_line_2"` + + Zip *string `json:"zip"` + + City *string `json:"city"` + + Country *string `json:"country"` + + VatIdentificationCode *string `json:"vat_identification_code"` + + CompanyIdentificationCode *string `json:"company_identification_code"` + // Lang: default value: unknown_language_code + Lang LanguageCode `json:"lang"` + + Resale *bool `json:"resale"` + // Deprecated + Questions *[]*UpdateContactRequestQuestion `json:"questions,omitempty"` + + ExtensionFr *ContactExtensionFR `json:"extension_fr"` + + ExtensionEu *ContactExtensionEU `json:"extension_eu"` + + WhoisOptIn *bool `json:"whois_opt_in"` + + State *string `json:"state"` + + ExtensionNl *ContactExtensionNL `json:"extension_nl"` +} + +// UpdateContact: update contact. +// You can edit the contact coordinates. +func (s *RegistrarAPI) UpdateContact(req *RegistrarAPIUpdateContactRequest, opts ...scw.RequestOption) (*Contact, error) { + var err error + + if fmt.Sprint(req.ContactID) == "" { + return nil, errors.New("field ContactID cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/domain/v2beta1/contacts/" + fmt.Sprint(req.ContactID) + "", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp Contact + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIListDomainsRequest struct { + Page *int32 `json:"-"` + + PageSize *uint32 `json:"-"` + // OrderBy: default value: domain_asc + OrderBy ListDomainsRequestOrderBy `json:"-"` + + Registrar *string `json:"-"` + // Status: default value: status_unknown + Status DomainStatus `json:"-"` + + ProjectID *string `json:"-"` + + OrganizationID *string `json:"-"` + + IsExternal *bool `json:"-"` + + Domain *string `json:"-"` +} + +// ListDomains: list domains. +// Returns a list of domains owned by the user. +func (s *RegistrarAPI) ListDomains(req *RegistrarAPIListDomainsRequest, opts ...scw.RequestOption) (*ListDomainsResponse, error) { + var err error + + defaultPageSize, exist := s.client.GetDefaultPageSize() + if (req.PageSize == nil || *req.PageSize == 0) && exist { + req.PageSize = &defaultPageSize + } + + query := url.Values{} + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "page_size", req.PageSize) + parameter.AddToQuery(query, "order_by", req.OrderBy) + parameter.AddToQuery(query, "registrar", req.Registrar) + parameter.AddToQuery(query, "status", req.Status) + parameter.AddToQuery(query, "project_id", req.ProjectID) + parameter.AddToQuery(query, "organization_id", req.OrganizationID) + parameter.AddToQuery(query, "is_external", req.IsExternal) + parameter.AddToQuery(query, "domain", req.Domain) + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/domains", + Query: query, + Headers: http.Header{}, + } + + var resp ListDomainsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIListRenewableDomainsRequest struct { + Page *int32 `json:"-"` + + PageSize *uint32 `json:"-"` + // OrderBy: default value: domain_asc + OrderBy ListRenewableDomainsRequestOrderBy `json:"-"` + + ProjectID *string `json:"-"` + + OrganizationID *string `json:"-"` +} + +// ListRenewableDomains: list scaleway domains that can or not be renewed. +// Returns a list of domains owned by the user with a renew status and if renewable, the maximum renew duration in years. +func (s *RegistrarAPI) ListRenewableDomains(req *RegistrarAPIListRenewableDomainsRequest, opts ...scw.RequestOption) (*ListRenewableDomainsResponse, error) { + var err error + + defaultPageSize, exist := s.client.GetDefaultPageSize() + if (req.PageSize == nil || *req.PageSize == 0) && exist { + req.PageSize = &defaultPageSize + } + + query := url.Values{} + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "page_size", req.PageSize) + parameter.AddToQuery(query, "order_by", req.OrderBy) + parameter.AddToQuery(query, "project_id", req.ProjectID) + parameter.AddToQuery(query, "organization_id", req.OrganizationID) + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/renewable-domains", + Query: query, + Headers: http.Header{}, + } + + var resp ListRenewableDomainsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIGetDomainRequest struct { + Domain string `json:"-"` +} + +// GetDomain: get domain. +// Returns a the domain with more informations. +func (s *RegistrarAPI) GetDomain(req *RegistrarAPIGetDomainRequest, opts ...scw.RequestOption) (*Domain, error) { + var err error + + if fmt.Sprint(req.Domain) == "" { + return nil, errors.New("field Domain cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/domains/" + fmt.Sprint(req.Domain) + "", + Headers: http.Header{}, + } + + var resp Domain + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIUpdateDomainRequest struct { + Domain string `json:"-"` + + // Precisely one of TechnicalContact, TechnicalContactID must be set. + TechnicalContactID *string `json:"technical_contact_id,omitempty"` + + // Precisely one of TechnicalContact, TechnicalContactID must be set. + TechnicalContact *NewContact `json:"technical_contact,omitempty"` + + // Precisely one of OwnerContact, OwnerContactID must be set. + OwnerContactID *string `json:"owner_contact_id,omitempty"` + + // Precisely one of OwnerContact, OwnerContactID must be set. + OwnerContact *NewContact `json:"owner_contact,omitempty"` + + // Precisely one of AdministrativeContact, AdministrativeContactID must be set. + AdministrativeContactID *string `json:"administrative_contact_id,omitempty"` + + // Precisely one of AdministrativeContact, AdministrativeContactID must be set. + AdministrativeContact *NewContact `json:"administrative_contact,omitempty"` +} + +// UpdateDomain: update a domain. +// Update the domain contacts or create a new one.
+// If you add the same contact for multiple roles. Only one ID will be created and used for all of them. +func (s *RegistrarAPI) UpdateDomain(req *RegistrarAPIUpdateDomainRequest, opts ...scw.RequestOption) (*Domain, error) { + var err error + + if fmt.Sprint(req.Domain) == "" { + return nil, errors.New("field Domain cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/domain/v2beta1/domains/" + fmt.Sprint(req.Domain) + "", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp Domain + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPILockDomainTransferRequest struct { + Domain string `json:"-"` +} + +// LockDomainTransfer: lock domain transfer. +// Lock domain transfer. A locked domain transfer can't be transferred and the auth code can't be requested. +func (s *RegistrarAPI) LockDomainTransfer(req *RegistrarAPILockDomainTransferRequest, opts ...scw.RequestOption) (*Domain, error) { + var err error + + if fmt.Sprint(req.Domain) == "" { + return nil, errors.New("field Domain cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/domains/" + fmt.Sprint(req.Domain) + "/lock-transfer", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp Domain + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIUnlockDomainTransferRequest struct { + Domain string `json:"-"` +} + +// UnlockDomainTransfer: unlock domain transfer. +// Unlock domain transfer. An unlocked domain can be transferred and the auth code can be requested for this. +func (s *RegistrarAPI) UnlockDomainTransfer(req *RegistrarAPIUnlockDomainTransferRequest, opts ...scw.RequestOption) (*Domain, error) { + var err error + + if fmt.Sprint(req.Domain) == "" { + return nil, errors.New("field Domain cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/domains/" + fmt.Sprint(req.Domain) + "/unlock-transfer", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp Domain + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIEnableDomainAutoRenewRequest struct { + Domain string `json:"-"` +} + +// EnableDomainAutoRenew: enable domain auto renew. +func (s *RegistrarAPI) EnableDomainAutoRenew(req *RegistrarAPIEnableDomainAutoRenewRequest, opts ...scw.RequestOption) (*Domain, error) { + var err error + + if fmt.Sprint(req.Domain) == "" { + return nil, errors.New("field Domain cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/domains/" + fmt.Sprint(req.Domain) + "/enable-auto-renew", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp Domain + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIDisableDomainAutoRenewRequest struct { + Domain string `json:"-"` +} + +// DisableDomainAutoRenew: disable domain auto renew. +func (s *RegistrarAPI) DisableDomainAutoRenew(req *RegistrarAPIDisableDomainAutoRenewRequest, opts ...scw.RequestOption) (*Domain, error) { + var err error + + if fmt.Sprint(req.Domain) == "" { + return nil, errors.New("field Domain cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/domains/" + fmt.Sprint(req.Domain) + "/disable-auto-renew", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp Domain + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIGetDomainAuthCodeRequest struct { + Domain string `json:"-"` +} + +// GetDomainAuthCode: return domain auth code. +// If possible, return the auth code for an unlocked domain transfer, or an error if the domain is locked. +// Some TLD may have a different procedure to retrieve the auth code, in that case, the information is given in the message field. +func (s *RegistrarAPI) GetDomainAuthCode(req *RegistrarAPIGetDomainAuthCodeRequest, opts ...scw.RequestOption) (*GetDomainAuthCodeResponse, error) { + var err error + + if fmt.Sprint(req.Domain) == "" { + return nil, errors.New("field Domain cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/domains/" + fmt.Sprint(req.Domain) + "/auth-code", + Headers: http.Header{}, + } + + var resp GetDomainAuthCodeResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIEnableDomainDNSSECRequest struct { + Domain string `json:"-"` + + DsRecord *DSRecord `json:"ds_record"` +} + +// EnableDomainDNSSEC: update domain DNSSEC. +// If your domain has the default Scaleway NS and uses another registrar, you have to update the DS record manually. +// For the algorithm, here are the code numbers for each type: +// - 1: RSAMD5 +// - 2: DIFFIE_HELLMAN +// - 3: DSA_SHA1 +// - 5: RSA_SHA1 +// - 6: DSA_NSEC3_SHA1 +// - 7: RSASHA1_NSEC3_SHA1 +// - 8: RSASHA256 +// - 10: RSASHA512 +// - 12: ECC_GOST +// - 13: ECDSAP256SHA256 +// - 14: ECDSAP384SHA384 +// +// And for the digest type: +// - 1: SHA_1 +// - 2: SHA_256 +// - 3: GOST_R_34_11_94 +// - 4: SHA_384. +func (s *RegistrarAPI) EnableDomainDNSSEC(req *RegistrarAPIEnableDomainDNSSECRequest, opts ...scw.RequestOption) (*Domain, error) { + var err error + + if fmt.Sprint(req.Domain) == "" { + return nil, errors.New("field Domain cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/domains/" + fmt.Sprint(req.Domain) + "/enable-dnssec", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp Domain + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIDisableDomainDNSSECRequest struct { + Domain string `json:"-"` +} + +// DisableDomainDNSSEC: disable domain DNSSEC. +func (s *RegistrarAPI) DisableDomainDNSSEC(req *RegistrarAPIDisableDomainDNSSECRequest, opts ...scw.RequestOption) (*Domain, error) { + var err error + + if fmt.Sprint(req.Domain) == "" { + return nil, errors.New("field Domain cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/domains/" + fmt.Sprint(req.Domain) + "/disable-dnssec", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp Domain + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPISearchAvailableDomainsRequest struct { + // Domains: a list of domain to search, TLD is optional. + Domains []string `json:"-"` + // Tlds: array of tlds to search on. + Tlds []string `json:"-"` + // StrictSearch: search exact match. + StrictSearch bool `json:"-"` +} + +// SearchAvailableDomains: search available domains. +// Search a domain (or at maximum, 10 domains). +// +// If the TLD list is empty or not set the search returns the results from the most popular TLDs. +func (s *RegistrarAPI) SearchAvailableDomains(req *RegistrarAPISearchAvailableDomainsRequest, opts ...scw.RequestOption) (*SearchAvailableDomainsResponse, error) { + var err error + + query := url.Values{} + parameter.AddToQuery(query, "domains", req.Domains) + parameter.AddToQuery(query, "tlds", req.Tlds) + parameter.AddToQuery(query, "strict_search", req.StrictSearch) + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/search-domains", + Query: query, + Headers: http.Header{}, + } + + var resp SearchAvailableDomainsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPICreateDomainHostRequest struct { + Domain string `json:"-"` + + Name string `json:"name"` + + IPs []net.IP `json:"ips"` +} + +// CreateDomainHost: create domain hostname with glue IPs. +func (s *RegistrarAPI) CreateDomainHost(req *RegistrarAPICreateDomainHostRequest, opts ...scw.RequestOption) (*Host, error) { + var err error + + if fmt.Sprint(req.Domain) == "" { + return nil, errors.New("field Domain cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "POST", + Path: "/domain/v2beta1/domains/" + fmt.Sprint(req.Domain) + "/hosts", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp Host + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIListDomainHostsRequest struct { + Domain string `json:"-"` + + Page *int32 `json:"-"` + + PageSize *uint32 `json:"-"` +} + +// ListDomainHosts: list domain hostnames with they glue IPs. +func (s *RegistrarAPI) ListDomainHosts(req *RegistrarAPIListDomainHostsRequest, opts ...scw.RequestOption) (*ListDomainHostsResponse, error) { + var err error + + defaultPageSize, exist := s.client.GetDefaultPageSize() + if (req.PageSize == nil || *req.PageSize == 0) && exist { + req.PageSize = &defaultPageSize + } + + query := url.Values{} + parameter.AddToQuery(query, "page", req.Page) + parameter.AddToQuery(query, "page_size", req.PageSize) + + if fmt.Sprint(req.Domain) == "" { + return nil, errors.New("field Domain cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "GET", + Path: "/domain/v2beta1/domains/" + fmt.Sprint(req.Domain) + "/hosts", + Query: query, + Headers: http.Header{}, + } + + var resp ListDomainHostsResponse + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIUpdateDomainHostRequest struct { + Domain string `json:"-"` + + Name string `json:"-"` + + IPs *[]string `json:"ips"` +} + +// UpdateDomainHost: update domain hostname with glue IPs. +func (s *RegistrarAPI) UpdateDomainHost(req *RegistrarAPIUpdateDomainHostRequest, opts ...scw.RequestOption) (*Host, error) { + var err error + + if fmt.Sprint(req.Domain) == "" { + return nil, errors.New("field Domain cannot be empty in request") + } + + if fmt.Sprint(req.Name) == "" { + return nil, errors.New("field Name cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "PATCH", + Path: "/domain/v2beta1/domains/" + fmt.Sprint(req.Domain) + "/hosts/" + fmt.Sprint(req.Name) + "", + Headers: http.Header{}, + } + + err = scwReq.SetBody(req) + if err != nil { + return nil, err + } + + var resp Host + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +type RegistrarAPIDeleteDomainHostRequest struct { + Domain string `json:"-"` + + Name string `json:"-"` +} + +// DeleteDomainHost: delete domain hostname. +func (s *RegistrarAPI) DeleteDomainHost(req *RegistrarAPIDeleteDomainHostRequest, opts ...scw.RequestOption) (*Host, error) { + var err error + + if fmt.Sprint(req.Domain) == "" { + return nil, errors.New("field Domain cannot be empty in request") + } + + if fmt.Sprint(req.Name) == "" { + return nil, errors.New("field Name cannot be empty in request") + } + + scwReq := &scw.ScalewayRequest{ + Method: "DELETE", + Path: "/domain/v2beta1/domains/" + fmt.Sprint(req.Domain) + "/hosts/" + fmt.Sprint(req.Name) + "", + Headers: http.Header{}, + } + + var resp Host + + err = s.client.Do(scwReq, &resp, opts...) + if err != nil { + return nil, err + } + return &resp, nil +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListDNSZonesResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListDNSZonesResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListDNSZonesResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.DNSZones = append(r.DNSZones, results.DNSZones...) + r.TotalCount += uint32(len(results.DNSZones)) + return uint32(len(results.DNSZones)), nil +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListDNSZoneRecordsResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListDNSZoneRecordsResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListDNSZoneRecordsResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Records = append(r.Records, results.Records...) + r.TotalCount += uint32(len(results.Records)) + return uint32(len(results.Records)), nil +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListDNSZoneVersionsResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListDNSZoneVersionsResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListDNSZoneVersionsResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Versions = append(r.Versions, results.Versions...) + r.TotalCount += uint32(len(results.Versions)) + return uint32(len(results.Versions)), nil +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListDNSZoneVersionRecordsResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListDNSZoneVersionRecordsResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListDNSZoneVersionRecordsResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Records = append(r.Records, results.Records...) + r.TotalCount += uint32(len(results.Records)) + return uint32(len(results.Records)), nil +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListSSLCertificatesResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListSSLCertificatesResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListSSLCertificatesResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Certificates = append(r.Certificates, results.Certificates...) + r.TotalCount += uint32(len(results.Certificates)) + return uint32(len(results.Certificates)), nil +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListTasksResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListTasksResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListTasksResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Tasks = append(r.Tasks, results.Tasks...) + r.TotalCount += uint32(len(results.Tasks)) + return uint32(len(results.Tasks)), nil +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListContactsResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListContactsResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListContactsResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Contacts = append(r.Contacts, results.Contacts...) + r.TotalCount += uint32(len(results.Contacts)) + return uint32(len(results.Contacts)), nil +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListDomainsResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListDomainsResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListDomainsResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Domains = append(r.Domains, results.Domains...) + r.TotalCount += uint32(len(results.Domains)) + return uint32(len(results.Domains)), nil +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListRenewableDomainsResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListRenewableDomainsResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListRenewableDomainsResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Domains = append(r.Domains, results.Domains...) + r.TotalCount += uint32(len(results.Domains)) + return uint32(len(results.Domains)), nil +} + +// UnsafeGetTotalCount should not be used +// Internal usage only +func (r *ListDomainHostsResponse) UnsafeGetTotalCount() uint32 { + return r.TotalCount +} + +// UnsafeAppend should not be used +// Internal usage only +func (r *ListDomainHostsResponse) UnsafeAppend(res interface{}) (uint32, error) { + results, ok := res.(*ListDomainHostsResponse) + if !ok { + return 0, errors.New("%T type cannot be appended to type %T", res, r) + } + + r.Hosts = append(r.Hosts, results.Hosts...) + r.TotalCount += uint32(len(results.Hosts)) + return uint32(len(results.Hosts)), nil +} diff --git a/vendor/github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1/domain_utils.go b/vendor/github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1/domain_utils.go new file mode 100644 index 0000000000..62089da8de --- /dev/null +++ b/vendor/github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1/domain_utils.go @@ -0,0 +1,81 @@ +package domain + +import ( + "fmt" + "time" + + "github.com/scaleway/scaleway-sdk-go/internal/async" + "github.com/scaleway/scaleway-sdk-go/internal/errors" + "github.com/scaleway/scaleway-sdk-go/scw" +) + +const ( + defaultRetryInterval = 15 * time.Second + defaultTimeout = 5 * time.Minute +) + +const ( + // ErrCodeNoSuchDNSZone for service response error code + // + // The specified dns zone does not exist. + ErrCodeNoSuchDNSZone = "NoSuchDNSZone" +) + +// WaitForDNSZoneRequest is used by WaitForDNSZone method. +type WaitForDNSZoneRequest struct { + DNSZone string + Timeout *time.Duration + RetryInterval *time.Duration +} + +func (s *API) WaitForDNSZone( + req *WaitForDNSZoneRequest, + opts ...scw.RequestOption, +) (*DNSZone, error) { + + timeout := defaultTimeout + if req.Timeout != nil { + timeout = *req.Timeout + } + retryInterval := defaultRetryInterval + if req.RetryInterval != nil { + retryInterval = *req.RetryInterval + } + + terminalStatus := map[DNSZoneStatus]struct{}{ + DNSZoneStatusActive: {}, + DNSZoneStatusLocked: {}, + DNSZoneStatusError: {}, + } + + dns, err := async.WaitSync(&async.WaitSyncConfig{ + Get: func() (interface{}, bool, error) { + // listing dns zones and take the first one + DNSZones, err := s.ListDNSZones(&ListDNSZonesRequest{ + DNSZone: req.DNSZone, + }, opts...) + + if err != nil { + return nil, false, err + } + + if len(DNSZones.DNSZones) == 0 { + return nil, true, fmt.Errorf(ErrCodeNoSuchDNSZone) + } + + Dns := DNSZones.DNSZones[0] + + _, isTerminal := terminalStatus[Dns.Status] + + return Dns, isTerminal, nil + }, + Timeout: timeout, + IntervalStrategy: async.LinearIntervalStrategy(retryInterval), + }) + + if err != nil { + return nil, errors.Wrap(err, "waiting for DNS failed") + } + + return dns.(*DNSZone), nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 0170219d16..b5cd531414 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -767,6 +767,7 @@ github.com/russross/blackfriday/v2 github.com/sahilm/fuzzy # github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 ## explicit; go 1.17 +github.com/scaleway/scaleway-sdk-go/api/domain/v2beta1 github.com/scaleway/scaleway-sdk-go/api/iam/v1alpha1 github.com/scaleway/scaleway-sdk-go/api/instance/v1 github.com/scaleway/scaleway-sdk-go/api/lb/v1