internal/loader/settings_client.go (142 lines of code) (raw):
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
package loader
import (
acpv1 "azappconfig/provider/api/v1"
"context"
"errors"
"fmt"
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/data/azappconfig"
"k8s.io/klog/v2"
)
//go:generate mockgen -destination=mocks/mock_settings_client.go -package mocks . SettingsClient
type SettingsResponse struct {
Settings []azappconfig.Setting
Etags map[acpv1.Selector][]*azcore.ETag
}
type EtagSettingsClient struct {
etags map[acpv1.Selector][]*azcore.ETag
}
type SentinelSettingsClient struct {
sentinel acpv1.Sentinel
etag *azcore.ETag
refreshInterval string
}
type SelectorSettingsClient struct {
selectors []acpv1.Selector
}
type SettingsClient interface {
GetSettings(ctx context.Context, client *azappconfig.Client) (*SettingsResponse, error)
}
func (s *EtagSettingsClient) GetSettings(ctx context.Context, client *azappconfig.Client) (*SettingsResponse, error) {
nullString := "\x00"
settingsResponse := &SettingsResponse{}
for filter, pageEtags := range s.etags {
if filter.KeyFilter != nil {
if filter.LabelFilter == nil {
filter.LabelFilter = &nullString // NUL is escaped to \x00 in golang
}
selector := azappconfig.SettingSelector{
KeyFilter: filter.KeyFilter,
LabelFilter: filter.LabelFilter,
Fields: azappconfig.AllSettingFields(),
}
conditions := make([]azcore.MatchConditions, 0)
for _, etag := range pageEtags {
conditions = append(conditions, azcore.MatchConditions{IfNoneMatch: etag})
}
pager := client.NewListSettingsPager(selector, &azappconfig.ListSettingsOptions{
MatchConditions: conditions,
})
pageCount := 0
for pager.More() {
pageCount++
page, err := pager.NextPage(context.Background())
if err != nil {
return nil, err
}
// If etag changed, return the non nil etags
if page.ETag != nil {
settingsResponse.Etags = make(map[acpv1.Selector][]*azcore.ETag)
return settingsResponse, nil
}
}
if pageCount != len(pageEtags) {
settingsResponse.Etags = make(map[acpv1.Selector][]*azcore.ETag)
return settingsResponse, nil
}
}
}
// no change in the settings, return nil etags
return settingsResponse, nil
}
func (s *SentinelSettingsClient) GetSettings(ctx context.Context, client *azappconfig.Client) (*SettingsResponse, error) {
sentinelSetting, err := client.GetSetting(ctx, s.sentinel.Key, &azappconfig.GetSettingOptions{Label: s.sentinel.Label, OnlyIfChanged: s.etag})
if err != nil {
var respErr *azcore.ResponseError
if errors.As(err, &respErr) {
var label string
if s.sentinel.Label == nil || *s.sentinel.Label == "\x00" { // NUL is escaped to \x00 in golang
label = "no"
} else {
label = fmt.Sprintf("'%s'", *s.sentinel.Label)
}
switch respErr.StatusCode {
case 404:
klog.Warningf("Sentinel key '%s' with %s label does not exists, revisit the sentinel after %s", s.sentinel.Key, label, s.refreshInterval)
return nil, nil
case 304:
klog.V(3).Infof("There's no change to the sentinel key '%s' with %s label , just exit and revisit the sentinel after %s", s.sentinel.Key, label, s.refreshInterval)
return &SettingsResponse{
Settings: []azappconfig.Setting{sentinelSetting.Setting},
}, nil
}
}
return nil, err
}
return &SettingsResponse{
Settings: []azappconfig.Setting{sentinelSetting.Setting},
}, nil
}
func (s *SelectorSettingsClient) GetSettings(ctx context.Context, client *azappconfig.Client) (*SettingsResponse, error) {
settings := make([]azappconfig.Setting, 0)
pageEtags := make(map[acpv1.Selector][]*azcore.ETag)
for _, filter := range s.selectors {
if filter.KeyFilter != nil {
selector := azappconfig.SettingSelector{
KeyFilter: filter.KeyFilter,
LabelFilter: filter.LabelFilter,
Fields: azappconfig.AllSettingFields(),
}
pager := client.NewListSettingsPager(selector, nil)
latestEtags := make([]*azcore.ETag, 0)
for pager.More() {
page, err := pager.NextPage(ctx)
if err != nil {
return nil, err
} else if page.Settings != nil {
settings = append(settings, page.Settings...)
latestEtags = append(latestEtags, page.ETag)
}
}
// update the etags for the filter
pageEtags[filter] = latestEtags
} else {
snapshot, err := client.GetSnapshot(ctx, *filter.SnapshotName, nil)
if err != nil {
return nil, err
}
if *snapshot.CompositionType != azappconfig.CompositionTypeKey {
return nil, fmt.Errorf("compositionType for the selected snapshot '%s' must be 'key', found '%s'", *filter.SnapshotName, *snapshot.CompositionType)
}
pager := client.NewListSettingsForSnapshotPager(*filter.SnapshotName, nil)
for pager.More() {
page, err := pager.NextPage(ctx)
if err != nil {
return nil, err
} else if page.Settings != nil {
settings = append(settings, page.Settings...)
}
}
}
}
return &SettingsResponse{
Settings: settings,
Etags: pageEtags,
}, nil
}