Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ require (
github.com/spf13/pflag v1.0.10
github.com/stackitcloud/stackit-sdk-go/core v0.26.0
github.com/stackitcloud/stackit-sdk-go/services/authorization v0.15.2
github.com/stackitcloud/stackit-sdk-go/services/dns v0.20.2
github.com/stackitcloud/stackit-sdk-go/services/dns v0.21.0
github.com/stackitcloud/stackit-sdk-go/services/iaas v1.12.0
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.13.0
github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.23.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -469,8 +469,8 @@ github.com/stackitcloud/stackit-sdk-go/core v0.26.0 h1:jQEb9gkehfp6VCP6TcYk7BI10
github.com/stackitcloud/stackit-sdk-go/core v0.26.0/go.mod h1:WU1hhxnjXw2EV7CYa1nlEvNpMiRY6CvmIOaHuL3pOaA=
github.com/stackitcloud/stackit-sdk-go/services/authorization v0.15.2 h1:b7WJ/vwxlVmNNX91kI3obqGcuoPAyaCbDL5aCMQ/sNg=
github.com/stackitcloud/stackit-sdk-go/services/authorization v0.15.2/go.mod h1:T/JF25XGJ3GqER/1L2N//DgY8x5tY7gA3N+/0nvmOWY=
github.com/stackitcloud/stackit-sdk-go/services/dns v0.20.2 h1:nMJRg1dKioOlMwXJnZZgIRwfTWYCksVA9GyfAVmib1g=
github.com/stackitcloud/stackit-sdk-go/services/dns v0.20.2/go.mod h1:FiYSv3D9rzgEVzi8Mpq5oYZBosrasa5uUYqVdEIbM1U=
github.com/stackitcloud/stackit-sdk-go/services/dns v0.21.0 h1:ZVkptfVCAqpaPWkE+WIopM9XdzqgbVcwmX5L1jZqqx8=
github.com/stackitcloud/stackit-sdk-go/services/dns v0.21.0/go.mod h1:FiYSv3D9rzgEVzi8Mpq5oYZBosrasa5uUYqVdEIbM1U=
github.com/stackitcloud/stackit-sdk-go/services/iaas v1.12.0 h1:H4V3H8qSKOaOalIrf4nAPDHhXnHYGs6SDGuK8Zj41Zo=
github.com/stackitcloud/stackit-sdk-go/services/iaas v1.12.0/go.mod h1:Ts06id0KejUlQWbpR+/rm+tKng6QkTuFV1VQTPJ4dA4=
github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.13.0 h1:UuLNwFHjJCpL11y4F7B9oBKtZkxpu01VkNPILNkpex4=
Expand Down
25 changes: 20 additions & 5 deletions pkg/stackit/client/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,12 @@ func (c *dnsClient) ListZones(ctx context.Context) ([]DNSZone, error) {
func (c *dnsClient) CreateOrUpdateRecordSet(ctx context.Context,
zoneID, name, recordType string, wantedRecords []string, ttl int64,
) error {
recordSet, err := c.findRecordSet(ctx, zoneID, name, recordType)
recordSetType, err := dns.NewRecordSetTypeFromValue(recordType)
if err != nil || recordSetType == nil {
return fmt.Errorf("invalid DNS record type %q: %w", recordType, err)
}

recordSet, err := c.findRecordSet(ctx, zoneID, name, recordSetType)
if err != nil {
return fmt.Errorf("failed to find record set: %w", err)
}
Expand All @@ -88,11 +93,16 @@ func (c *dnsClient) CreateOrUpdateRecordSet(ctx context.Context,
return err
}

payloadType, err := dns.NewCreateRecordSetPayloadTypeFromValue(recordType)
if err != nil || payloadType == nil {
Comment thread
nschad marked this conversation as resolved.
return fmt.Errorf("invalid DNS record type %q for create payload: %w", recordType, err)
}

if recordSet == nil {
_, err := c.api.CreateRecordSet(ctx, c.projectID, zoneID).CreateRecordSetPayload(dns.CreateRecordSetPayload{
Name: name,
Records: wantedRecordsPayload,
Type: recordType,
Type: *payloadType,
Ttl: new(cacheTTL),
}).Execute()
if err != nil {
Expand All @@ -119,7 +129,12 @@ func (c *dnsClient) CreateOrUpdateRecordSet(ctx context.Context,
}

func (c *dnsClient) DeleteRecordSet(ctx context.Context, zoneID, name, recordType string) error {
recordSet, err := c.findRecordSet(ctx, zoneID, name, recordType)
recordSetType, err := dns.NewRecordSetTypeFromValue(recordType)
if err != nil || recordSetType == nil {
return fmt.Errorf("invalid DNS record type %q: %w", recordType, err)
}

recordSet, err := c.findRecordSet(ctx, zoneID, name, recordSetType)
if err != nil {
return fmt.Errorf("failed to find record set: %w", err)
}
Expand All @@ -134,7 +149,7 @@ func (c *dnsClient) DeleteRecordSet(ctx context.Context, zoneID, name, recordTyp
return nil
}

func (c *dnsClient) findRecordSet(ctx context.Context, zoneID, name, recordType string) (*dns.RecordSet, error) {
func (c *dnsClient) findRecordSet(ctx context.Context, zoneID, name string, recordType *dns.RecordSetType) (*dns.RecordSet, error) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: Is there a reason why dns.RecordSetType is ptr to string? Seems overkill

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems easier to just have recordType dns.RecordSetType

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

then i have to create an explicit nil check in the other function.
Doing it like so is cleaner since i can do the nil check when i deref the pointer :)

resp, err := c.api.ListRecordSets(ctx, c.projectID, zoneID).Execute()
if err != nil {
return nil, err
Expand All @@ -148,7 +163,7 @@ func (c *dnsClient) findRecordSet(ctx context.Context, zoneID, name, recordType
if strings.TrimSuffix(recordSet.GetName(), ".") != name {
continue
}
if recordSet.GetType() != recordType {
if recordType == nil || recordSet.GetType() != *recordType {
continue
}
return &recordSet, nil
Expand Down
94 changes: 50 additions & 44 deletions pkg/stackit/client/dns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,46 +52,52 @@ var _ = Describe("DNSClient", func() {
})

Describe("CreateOrUpdate Record", func() {
BeforeEach(func() {
mockAPI.EXPECT().ListRecordSets(ctx, client.projectID, "zone1").Return(dns.ApiListRecordSetsRequest{ApiService: mockAPI})
mockAPI.EXPECT().ListRecordSetsExecute(gomock.Any()).Return(&dns.ListRecordSetsResponse{
RrSets: []dns.RecordSet{
{
Name: "test.example.com.",
Active: new(true),
Type: "A",
Records: []dns.Record{{Content: "1.1.1.1"}},
Id: "some-uuid",
Ttl: 300,
},
{
Name: "test.example.com.",
Active: new(false),
Type: "A",
Records: []dns.Record{{Content: "4.4.4.4"}},
Id: "some-uuid2",
Ttl: 300,
Context("with a supported record type", func() {
BeforeEach(func() {
mockAPI.EXPECT().ListRecordSets(ctx, client.projectID, "zone1").Return(dns.ApiListRecordSetsRequest{ApiService: mockAPI})
mockAPI.EXPECT().ListRecordSetsExecute(gomock.Any()).Return(&dns.ListRecordSetsResponse{
RrSets: []dns.RecordSet{
{
Name: "test.example.com.",
Active: new(true),
Type: dns.RECORDSETTYPE_A,
Records: []dns.Record{{Content: "1.1.1.1"}},
Id: "some-uuid",
Ttl: 300,
},
{
Name: "test.example.com.",
Active: new(false),
Type: dns.RECORDSETTYPE_A,
Records: []dns.Record{{Content: "4.4.4.4"}},
Id: "some-uuid2",
Ttl: 300,
},
},
},
}, nil)
})
}, nil)
})

It("should create a new record set if it does not exist", func() {
mockAPI.EXPECT().CreateRecordSet(ctx, client.projectID, "zone1").Return(dns.ApiCreateRecordSetRequest{ApiService: mockAPI})
mockAPI.EXPECT().CreateRecordSetExecute(gomock.Any()).Return(nil, nil)
It("should create a new record set if it does not exist", func() {
mockAPI.EXPECT().CreateRecordSet(ctx, client.projectID, "zone1").Return(dns.ApiCreateRecordSetRequest{ApiService: mockAPI})
mockAPI.EXPECT().CreateRecordSetExecute(gomock.Any()).Return(nil, nil)

Expect(client.CreateOrUpdateRecordSet(ctx, "zone1", "new.example.com.", "A", []string{"1.1.1.1"}, 300)).To(Succeed())
})
Expect(client.CreateOrUpdateRecordSet(ctx, "zone1", "new.example.com.", string(dns.RECORDSETTYPE_A), []string{"1.1.1.1"}, 300)).To(Succeed())
})

It("should update the existing record set if it exists and records are different", func() {
mockAPI.EXPECT().PartialUpdateRecordSet(ctx, client.projectID, "zone1", "some-uuid").Return(dns.ApiPartialUpdateRecordSetRequest{ApiService: mockAPI})
mockAPI.EXPECT().PartialUpdateRecordSetExecute(gomock.Any()).Return(nil, nil)
It("should update the existing record set if it exists and records are different", func() {
mockAPI.EXPECT().PartialUpdateRecordSet(ctx, client.projectID, "zone1", "some-uuid").Return(dns.ApiPartialUpdateRecordSetRequest{ApiService: mockAPI})
mockAPI.EXPECT().PartialUpdateRecordSetExecute(gomock.Any()).Return(nil, nil)

Expect(client.CreateOrUpdateRecordSet(ctx, "zone1", "test.example.com.", "A", []string{"4.4.4.4"}, 300)).To(Succeed())
Expect(client.CreateOrUpdateRecordSet(ctx, "zone1", "test.example.com.", string(dns.RECORDSETTYPE_A), []string{"4.4.4.4"}, 300)).To(Succeed())
})

It("should do nothing if the existing record set has the same records and TTL", func() {
Expect(client.CreateOrUpdateRecordSet(ctx, "zone1", "test.example.com.", string(dns.RECORDSETTYPE_A), []string{"1.1.1.1"}, 300)).To(Succeed())
})
})

It("should do nothing if the existing record set has the same records and TTL", func() {
Expect(client.CreateOrUpdateRecordSet(ctx, "zone1", "test.example.com.", "A", []string{"1.1.1.1"}, 300)).To(Succeed())
It("should reject unsupported record types before calling the API", func() {
Expect(client.CreateOrUpdateRecordSet(ctx, "zone1", "test.example.com.", "UNSUPPORTED", []string{"1.1.1.1"}, 300)).To(MatchError(ContainSubstring("invalid DNS record type \"UNSUPPORTED\"")))
})
})

Expand All @@ -102,28 +108,28 @@ var _ = Describe("DNSClient", func() {
RrSets: []dns.RecordSet{{
Name: "test.example.com.",
Active: new(true),
Type: "A",
Type: dns.RECORDSETTYPE_A,
Id: "some-uuid",
}},
}, nil)
})

It("should do nothing if the record set does not exist", func() {
Expect(client.DeleteRecordSet(ctx, "zone1", "nonexistent.example.com.", "A")).To(Succeed())
Expect(client.DeleteRecordSet(ctx, "zone1", "nonexistent.example.com.", string(dns.RECORDSETTYPE_A))).To(Succeed())
})

It("should delete the record set if it exists", func() {
mockAPI.EXPECT().DeleteRecordSet(ctx, client.projectID, "zone1", "some-uuid").Return(dns.ApiDeleteRecordSetRequest{ApiService: mockAPI})
mockAPI.EXPECT().DeleteRecordSetExecute(gomock.Any()).Return(nil, nil)

Expect(client.DeleteRecordSet(ctx, "zone1", "test.example.com.", "A")).To(Succeed())
Expect(client.DeleteRecordSet(ctx, "zone1", "test.example.com.", string(dns.RECORDSETTYPE_A))).To(Succeed())
})

It("should delete the record even if a non-FQDN is specified", func() {
mockAPI.EXPECT().DeleteRecordSet(ctx, client.projectID, "zone1", "some-uuid").Return(dns.ApiDeleteRecordSetRequest{ApiService: mockAPI})
mockAPI.EXPECT().DeleteRecordSetExecute(gomock.Any()).Return(nil, nil)

Expect(client.DeleteRecordSet(ctx, "zone1", "test.example.com", "A")).To(Succeed())
Expect(client.DeleteRecordSet(ctx, "zone1", "test.example.com", string(dns.RECORDSETTYPE_A))).To(Succeed())
})
})

Expand All @@ -133,28 +139,28 @@ var _ = Describe("DNSClient", func() {
{
Name: "active.example.com.",
Active: new(true),
Type: "A",
Type: dns.RECORDSETTYPE_A,
Records: []dns.Record{{Content: "1.1.1.1"}},
Id: "active-a-uuid",
},
{
Name: "active2.example.com.",
Active: new(true),
Type: "A",
Type: dns.RECORDSETTYPE_A,
Records: []dns.Record{{Content: "1.1.1.1"}},
Id: "active2-a-uuid",
},
{
Name: "active.example.com.",
Active: new(true),
Type: "TXT",
Type: dns.RECORDSETTYPE_TXT,
Records: []dns.Record{{Content: "hello-world"}},
Id: "active-txt-uuid",
},
{
Name: "inactive.example.com.",
Active: new(false),
Type: "A",
Type: dns.RECORDSETTYPE_A,
Records: []dns.Record{{Content: "2.2.2.2"}},
Id: "inactive-a-uuid",
},
Expand All @@ -169,21 +175,21 @@ var _ = Describe("DNSClient", func() {
})

It("should return the correct A recordSet", func() {
recordSet, err := client.findRecordSet(ctx, "zone1", "active.example.com.", "A")
recordSet, err := client.findRecordSet(ctx, "zone1", "active.example.com.", dns.RECORDSETTYPE_A.Ptr())
Expect(err).ToNot(HaveOccurred())
Expect(recordSet).ToNot(BeNil())
Expect(recordSet.GetId()).To(Equal("active-a-uuid"))
})

It("should return the correct TXT recordSet", func() {
recordSet, err := client.findRecordSet(ctx, "zone1", "active.example.com.", "TXT")
recordSet, err := client.findRecordSet(ctx, "zone1", "active.example.com.", dns.RECORDSETTYPE_TXT.Ptr())
Expect(err).ToNot(HaveOccurred())
Expect(recordSet).ToNot(BeNil())
Expect(recordSet.GetId()).To(Equal("active-txt-uuid"))
})

It("should return nil if nothing matches", func() {
recordSet, err := client.findRecordSet(ctx, "zone1", "non-existant.example.com.", "A")
recordSet, err := client.findRecordSet(ctx, "zone1", "non-existant.example.com.", dns.RECORDSETTYPE_A.Ptr())
Expect(err).ToNot(HaveOccurred())
Expect(recordSet).To(BeNil())
})
Expand Down