Skip to content
Open
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
26 changes: 18 additions & 8 deletions drivers/resipsgcp_dnsalias/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ type (
}

mgr struct {
alias alias
api apiProvider
log *plog.Logger
alias alias
api apiProvider
log *plog.Logger
CacheTTL time.Duration
}

// alias decoupled from sgcp.Alias to allow for future changes
Expand All @@ -63,6 +64,8 @@ type (
}
)

const defaultCacheTTL = 30 * time.Second

func New() resource.Driver {
return &T{}
}
Expand Down Expand Up @@ -110,8 +113,9 @@ func (t *T) Configure() error {

func (t *T) configureMgr(cfg *sgcp.Config) error {
mgr := &mgr{
alias: alias{UUID: t.UUID, Name: t.Name, Target: t.Target, ZoneID: t.ZoneID},
log: t.Log(),
alias: alias{UUID: t.UUID, Name: t.Name, Target: t.Target, ZoneID: t.ZoneID},
log: t.Log(),
CacheTTL: defaultCacheTTL,
}
if t.api != nil {
// allow custom api for tests
Expand Down Expand Up @@ -143,20 +147,26 @@ func (t *T) configureMgr(cfg *sgcp.Config) error {
}

func (t *T) Start(ctx context.Context) error {
// TODO: implement cache cleanup
if err := t.mgr.cacheClear(); err != nil {
t.Log().Debugf("cache clear error: %s", err)
}
return t.mgr.createOrUpdate(ctx, t.Target)
}

func (t *T) Stop(ctx context.Context) error {
// TODO: implement cache cleanup
if err := t.mgr.cacheClear(); err != nil {
t.Log().Debugf("cache clear error: %s", err)
}
if t.UUID != "" {
return t.mgr.createOrUpdate(ctx, t.noneTarget)
}
return t.mgr.delete(ctx)
}

func (t *T) Status(ctx context.Context) status.T {
// TODO: implement cache cleanup if command is not called from the scheduler
if err := t.mgr.cacheClear(); err != nil {
t.Log().Debugf("cache clear error: %s", err)
}
aliases, err := t.mgr.getAliases(ctx)
if err != nil {
t.StatusLog().Error("get alias failed: %s", err)
Expand Down
9 changes: 9 additions & 0 deletions drivers/resipsgcp_dnsalias/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ func TestStatus(t *testing.T) {
drv.Target = tc.resTarget
drv.ZoneID = tc.resZoneID
require.NoError(t, drv.Configure())
drv.mgr.CacheTTL = 0

dStatus := drv.Status(ctx)
assert.Equalf(t, tc.expectedStatus, dStatus, "expected %s, got %s", tc.expectedStatus, dStatus)
Expand Down Expand Up @@ -300,6 +301,7 @@ func TestStart(t *testing.T) {
drv.Target = "foo-target"
drv.ZoneID = "z1"
require.NoError(t, drv.Configure())
drv.mgr.CacheTTL = 0

t.Log("verify alias doesn't exits")
alias, ok := db.Search("z1", "foo", "")
Expand Down Expand Up @@ -334,6 +336,7 @@ func TestStart(t *testing.T) {
drv.Target = "target1"
drv.ZoneID = "z1"
require.NoError(t, drv.Configure())
drv.mgr.CacheTTL = 0

t.Log("verify alias initially exits")
alias, ok := db.Search("z1", "name1", "uuid1")
Expand Down Expand Up @@ -362,6 +365,7 @@ func TestStart(t *testing.T) {
drv.Target = "newTarget2"
drv.ZoneID = "z1"
require.NoError(t, drv.Configure())
drv.mgr.CacheTTL = 0

t.Log("verify alias exits initially, with alternate target")
alias, ok := db.Search("z1", "name2", "uuid2")
Expand Down Expand Up @@ -452,6 +456,7 @@ func TestStop(t *testing.T) {
drv.Target = "target"
drv.ZoneID = "z1"
require.NoError(t, drv.Configure())
drv.mgr.CacheTTL = 0

t.Log("verify alias doesn't exits")
_, ok := db.Search(drv.ZoneID, drv.Name, drv.UUID)
Expand All @@ -477,6 +482,7 @@ func TestStop(t *testing.T) {
drv.Target = "target"
drv.ZoneID = "z1"
require.NoError(t, drv.Configure())
drv.mgr.CacheTTL = 0

t.Log("verify alias doesn't exits")
_, ok := db.Search(drv.ZoneID, drv.Name, drv.UUID)
Expand All @@ -500,6 +506,7 @@ func TestStop(t *testing.T) {
drv.Target = "target1"
drv.ZoneID = "z1"
require.NoError(t, drv.Configure())
drv.mgr.CacheTTL = 0

t.Log("verify initial exits")
alias, ok := db.Search(drv.ZoneID, drv.Name, drv.UUID)
Expand Down Expand Up @@ -531,6 +538,7 @@ func TestStop(t *testing.T) {
drv.Target = "none.xxx"
drv.ZoneID = "z1"
require.NoError(t, drv.Configure())
drv.mgr.CacheTTL = 0

t.Log("verify initial exits with target none")
alias, ok := db.Search(drv.ZoneID, drv.Name, drv.UUID)
Expand Down Expand Up @@ -561,6 +569,7 @@ func TestStop(t *testing.T) {
drv.Target = "target1"
drv.ZoneID = "z1"
require.NoError(t, drv.Configure())
drv.mgr.CacheTTL = 0

t.Log("verify initial exits")
initial, ok := db.Search(drv.ZoneID, drv.Name, drv.UUID)
Expand Down
68 changes: 57 additions & 11 deletions drivers/resipsgcp_dnsalias/mgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"net/http"

"github.com/opensvc/om3/v3/util/ageingcache"
"github.com/opensvc/om3/v3/util/sgcp"
)

Expand All @@ -27,6 +28,9 @@ func (m *mgr) createOrUpdate(ctx context.Context, target string) error {
return fmt.Errorf("create alias: %w", err)
} else {
m.alias = *v
if err := m.cacheClear(); err != nil {
m.log.Debugf("cache clear error: %s", err)
}
return nil
}
}
Expand All @@ -51,6 +55,9 @@ func (m *mgr) createOrUpdate(ctx context.Context, target string) error {
return fmt.Errorf("update alias unexpected nil")
} else {
m.alias = *v
if err := m.cacheClear(); err != nil {
m.log.Debugf("cache clear error: %s", err)
}
return nil
}
}
Expand All @@ -72,20 +79,37 @@ func (m *mgr) delete(ctx context.Context) error {
if err := m.api.DeleteAlias(ctx, alias.ZoneID, alias.UUID); err != nil {
return fmt.Errorf("delete alias: %w", err)
}
if err := m.cacheClear(); err != nil {
m.log.Debugf("cache clear error: %s", err)
}
return nil
}

// getAliases retrieves a list of aliases for the specified zone, name, and UUID or returns an error if unsuccessful.
func (m *mgr) getAliases(ctx context.Context) ([]sgcp.Alias, error) {
// TODO: Use ageing cache
method, url, code, data, err := m.api.GetAliases(ctx, m.alias.ZoneID, m.alias.Name, m.alias.UUID)
if err != nil {
return nil, err
if m.CacheTTL <= 0 {
data, err := m.getAliasesFactory(ctx)()
if err != nil {
return nil, err
}
if data == nil || string(data) == "null" {
return nil, nil
}
var resp aliasListResponse
if err := json.Unmarshal(data, &resp); err != nil {
return nil, fmt.Errorf("decode aliases: %w", err)
}
return resp.CnameRecords, nil
}
if err := m.api.CheckStatusCode(method, url, code, http.StatusOK, http.StatusNotFound); err != nil {

o := ageingcache.NewOutputter(m.getAliasesFactory(ctx))
sig := m.cacheSig()
data, err := ageingcache.Output(o, sig, m.CacheTTL)
if err != nil {
m.log.Debugf("getAliases cache miss: %s", err)
return nil, err
}
if code == http.StatusNotFound {
if data == nil || string(data) == "null" {
return nil, nil
}
var resp aliasListResponse
Expand All @@ -95,7 +119,22 @@ func (m *mgr) getAliases(ctx context.Context) ([]sgcp.Alias, error) {
return resp.CnameRecords, nil
}

// create creates a new alias with the specified target and returns the created alias or an error if the operation fails.
func (m *mgr) getAliasesFactory(ctx context.Context) func() ([]byte, error) {
return func() ([]byte, error) {
method, url, code, data, err := m.api.GetAliases(ctx, m.alias.ZoneID, m.alias.Name, m.alias.UUID)
if err != nil {
return nil, err
}
if err := m.api.CheckStatusCode(method, url, code, http.StatusOK, http.StatusNotFound); err != nil {
return nil, err
}
if code == http.StatusNotFound {
return []byte("null"), nil
}
return data, nil
}
}

func (m *mgr) create(ctx context.Context, target string) (*alias, error) {
v, err := m.api.CreateAlias(ctx, m.alias.ZoneID, m.alias.Name, target)
if err != nil {
Expand All @@ -104,7 +143,6 @@ func (m *mgr) create(ctx context.Context, target string) (*alias, error) {
return toAlias(v), nil
}

// update modifies an existing alias with the specified parameters and returns the updated alias or an error if any occurs.
func (m *mgr) update(ctx context.Context, zoneID, aliasUUID, aliasName, target string) (*alias, error) {
v, err := m.api.UpdateAlias(ctx, zoneID, aliasUUID, aliasName, target)
if err != nil {
Expand All @@ -113,7 +151,6 @@ func (m *mgr) update(ctx context.Context, zoneID, aliasUUID, aliasName, target s
return toAlias(v), nil
}

// toAlias converts a sgcp.Alias object to an alias object by mapping corresponding fields.
func toAlias(v *sgcp.Alias) *alias {
return &alias{
UUID: v.UUID,
Expand All @@ -124,8 +161,6 @@ func toAlias(v *sgcp.Alias) *alias {
}
}

// Equal compares two alias objects and returns true if they are equal, or false otherwise.
// It doesn't compare the FQDN field.
func (a *alias) Equal(b *alias) bool {
if a == nil && b == nil {
return true
Expand All @@ -138,3 +173,14 @@ func (a *alias) Equal(b *alias) bool {
a.Target == b.Target &&
a.ZoneID == b.ZoneID
}

func (m *mgr) cacheSig() string {
return fmt.Sprintf("dnsalias:%s:%s:%s", m.alias.ZoneID, m.alias.Name, m.alias.UUID)
}

func (m *mgr) cacheClear() error {
if m.CacheTTL <= 0 {
return nil
}
return ageingcache.Clear(m.cacheSig())
}
Loading