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
5 changes: 5 additions & 0 deletions config/samples/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ provider:
# If you want to enable the sync, set it to a positive value.
init_sync_delay: 20m # The initial delay before the first sync, only used when the controller is started.
# The default value is 20 minutes.
exclude_resource_type: [] # Resource types to exclude from full reconciliation sweeps.
# Resources of these types that exist in APISIX but have no corresponding
# CRD will not be deleted during periodic syncs.
# Supported values mirror the ADC resource types, e.g.: Consumer, ConsumerGroup.
# The default value is [] (empty — all resource types are reconciled).

webhook:
enable: false # Whether to enable the webhook server.
Expand Down
6 changes: 6 additions & 0 deletions docs/en/latest/reference/configuration-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,10 @@ provider:
# If you want to enable the sync, set it to a positive value.
init_sync_delay: 20m # The initial delay before the first sync, only used when the controller is started.
# The default value is 20 minutes.

exclude_resource_type: [] # Resource types to exclude from full reconciliation sweeps.
# Resources of these types that exist in APISIX but have no corresponding
# CRD will not be deleted during periodic syncs.
# Supported values mirror the ADC resource types, e.g.: Consumer, ConsumerGroup.
# The default value is [] (empty — all resource types are reconciled).
```
42 changes: 23 additions & 19 deletions internal/adc/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,13 @@ func isConfVersionRejection(err error) bool {
const confVersionField = "conf_version"

type Task struct {
Key types.NamespacedNameKind
Name string
Labels map[string]string
Configs map[types.NamespacedNameKind]adctypes.Config
ResourceTypes []string
Resources *adctypes.Resources
Key types.NamespacedNameKind
Name string
Labels map[string]string
Configs map[types.NamespacedNameKind]adctypes.Config
ResourceTypes []string
ExcludeResourceType []string
Resources *adctypes.Resources
}

// MarshalLog implements logr.Marshaler so logging a Task never dumps the
Expand Down Expand Up @@ -198,22 +199,24 @@ func (c *Client) applySync(ctx context.Context, args Task, delta StoreDelta) err

if len(delta.Deleted) > 0 {
if err := c.sync(ctx, Task{
Name: args.Name,
Labels: args.Labels,
ResourceTypes: args.ResourceTypes,
Configs: delta.Deleted,
Name: args.Name,
Labels: args.Labels,
ResourceTypes: args.ResourceTypes,
ExcludeResourceType: args.ExcludeResourceType,
Configs: delta.Deleted,
}); err != nil {
c.log.Error(err, "failed to sync deleted configs", "args", args, "delta", delta)
}
}

if len(delta.Applied) > 0 {
return c.sync(ctx, Task{
Name: args.Name,
Labels: args.Labels,
ResourceTypes: args.ResourceTypes,
Configs: delta.Applied,
Resources: args.Resources,
Name: args.Name,
Labels: args.Labels,
ResourceTypes: args.ResourceTypes,
ExcludeResourceType: args.ExcludeResourceType,
Configs: delta.Applied,
Resources: args.Resources,
})
}
return nil
Expand Down Expand Up @@ -259,7 +262,7 @@ func (c *Client) Validate(ctx context.Context, task Task) error {
pkgmetrics.RecordFileIODuration("prepare_sync_file", adctypes.StatusSuccess, time.Since(fileIOStart).Seconds())
defer cleanup()

args2 := BuildADCExecuteArgs(syncFilePath, task.Labels, task.ResourceTypes)
args2 := BuildADCExecuteArgs(syncFilePath, task.Labels, task.ResourceTypes, task.ExcludeResourceType)

var errs types.ADCValidationErrors
for _, config := range task.Configs {
Expand All @@ -282,7 +285,7 @@ func (c *Client) Validate(ctx context.Context, task Task) error {
return nil
}

func (c *Client) Sync(ctx context.Context) (map[string]types.ADCExecutionErrors, error) {
func (c *Client) Sync(ctx context.Context, excludeResourceType []string) (map[string]types.ADCExecutionErrors, error) {
c.syncMu.Lock()
defer c.syncMu.Unlock()
c.log.Info("syncing all resources")
Expand Down Expand Up @@ -316,7 +319,8 @@ func (c *Client) Sync(ctx context.Context) (map[string]types.ADCExecutionErrors,
Configs: map[types.NamespacedNameKind]adctypes.Config{
{}: config,
},
Resources: resources,
ExcludeResourceType: excludeResourceType,
Resources: resources,
}); err != nil {
c.log.Error(err, "failed to sync resources", "name", name)
failedConfigs = append(failedConfigs, name)
Expand Down Expand Up @@ -408,7 +412,7 @@ func (c *Client) sync(ctx context.Context, task Task) error {
defer cleanup()
c.log.V(1).Info("prepared sync file", "path", syncFilePath)

args := BuildADCExecuteArgs(syncFilePath, task.Labels, task.ResourceTypes)
args := BuildADCExecuteArgs(syncFilePath, task.Labels, task.ResourceTypes, task.ExcludeResourceType)

for _, config := range task.Configs {
// Record sync duration for each config
Expand Down
44 changes: 28 additions & 16 deletions internal/adc/client/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,20 @@ type ADCExecutor interface {
Validate(ctx context.Context, config adctypes.Config, args []string) error
}

func BuildADCExecuteArgs(filePath string, labels map[string]string, types []string) []string {
func BuildADCExecuteArgs(filePath string, labels map[string]string, includeTypes []string, excludeTypes []string) []string {
args := []string{
"sync",
"-f", filePath,
}
for k, v := range labels {
args = append(args, "--label-selector", k+"="+v)
}
for _, t := range types {
for _, t := range includeTypes {
args = append(args, "--include-resource-type", t)
}
for _, t := range excludeTypes {
args = append(args, "--exclude-resource-type", t)
}
return args
}

Expand All @@ -83,6 +86,7 @@ type ADCServerOpts struct {
Token string `json:"token"`
LabelSelector map[string]string `json:"labelSelector,omitempty"`
IncludeResourceType []string `json:"includeResourceType,omitempty"`
ExcludeResourceType []string `json:"excludeResourceType,omitempty"`
TlsSkipVerify *bool `json:"tlsSkipVerify,omitempty"`
CacheKey string `json:"cacheKey"`
// BypassCache is only accepted by the /sync task of ADC >= 0.27.0. Both ADC task
Expand Down Expand Up @@ -231,8 +235,8 @@ func (e *HTTPADCExecutor) runHTTPSyncForSingleServer(ctx context.Context, server
ctx, cancel := context.WithTimeout(ctx, e.httpClient.Timeout)
defer cancel()

// Parse args to extract labels, types, and file path
labels, types, filePath, err := e.parseArgs(args)
// Parse args to extract labels, include/exclude types, and file path
labels, includeTypes, excludeTypes, filePath, err := e.parseArgs(args)
if err != nil {
return fmt.Errorf("failed to parse args: %w", err)
}
Expand All @@ -244,7 +248,7 @@ func (e *HTTPADCExecutor) runHTTPSyncForSingleServer(ctx context.Context, server
}

// Build HTTP request
req, err := e.buildHTTPRequest(ctx, serverAddr, config, labels, types, resources, http.MethodPut, pathSync)
req, err := e.buildHTTPRequest(ctx, serverAddr, config, labels, includeTypes, excludeTypes, resources, http.MethodPut, pathSync)
if err != nil {
return fmt.Errorf("failed to build HTTP request: %w", err)
}
Expand All @@ -268,7 +272,7 @@ func (e *HTTPADCExecutor) runHTTPValidateForSingleServer(ctx context.Context, se
ctx, cancel := context.WithTimeout(ctx, e.httpClient.Timeout)
defer cancel()

labels, types, filePath, err := e.parseArgs(args)
labels, includeTypes, excludeTypes, filePath, err := e.parseArgs(args)
if err != nil {
return fmt.Errorf("failed to parse args: %w", err)
}
Expand All @@ -278,7 +282,7 @@ func (e *HTTPADCExecutor) runHTTPValidateForSingleServer(ctx context.Context, se
return fmt.Errorf("failed to load resources from file %s: %w", filePath, err)
}

req, err := e.buildHTTPRequest(ctx, serverAddr, config, labels, types, resources, http.MethodPut, pathValidate)
req, err := e.buildHTTPRequest(ctx, serverAddr, config, labels, includeTypes, excludeTypes, resources, http.MethodPut, pathValidate)
if err != nil {
return fmt.Errorf("failed to build validate request: %w", err)
}
Expand All @@ -296,10 +300,11 @@ func (e *HTTPADCExecutor) runHTTPValidateForSingleServer(ctx context.Context, se
return e.handleHTTPValidateResponse(resp, serverAddr)
}

// parseArgs parses the command line arguments to extract labels, types, and file path
func (e *HTTPADCExecutor) parseArgs(args []string) (map[string]string, []string, string, error) {
// parseArgs parses the command line arguments to extract labels, include/exclude types, and file path
func (e *HTTPADCExecutor) parseArgs(args []string) (map[string]string, []string, []string, string, error) {
labels := make(map[string]string)
var types []string
var includeTypes []string
var excludeTypes []string
var filePath string

for i := 0; i < len(args); i++ {
Expand All @@ -320,17 +325,22 @@ func (e *HTTPADCExecutor) parseArgs(args []string) (map[string]string, []string,
}
case "--include-resource-type":
if i+1 < len(args) {
types = append(types, args[i+1])
includeTypes = append(includeTypes, args[i+1])
i++
}
case "--exclude-resource-type":
if i+1 < len(args) {
excludeTypes = append(excludeTypes, args[i+1])
i++
}
}
}

if filePath == "" {
return nil, nil, "", errors.New("file path not found in args")
return nil, nil, nil, "", errors.New("file path not found in args")
}

return labels, types, filePath, nil
return labels, includeTypes, excludeTypes, filePath, nil
}

// loadResourcesFromFile loads ADC resources from the specified file
Expand All @@ -349,7 +359,7 @@ func (e *HTTPADCExecutor) loadResourcesFromFile(filePath string) (*adctypes.Reso
}

// buildHTTPRequest builds the HTTP request for ADC Server
func (e *HTTPADCExecutor) buildHTTPRequest(ctx context.Context, serverAddr string, config adctypes.Config, labels map[string]string, types []string, resources *adctypes.Resources, method string, path string) (*http.Request, error) {
func (e *HTTPADCExecutor) buildHTTPRequest(ctx context.Context, serverAddr string, config adctypes.Config, labels map[string]string, includeTypes []string, excludeTypes []string, resources *adctypes.Resources, method string, path string) (*http.Request, error) {
// Prepare request body
tlsVerify := config.TlsVerify
bypassCache := path == pathSync && config.BypassCache
Expand All @@ -360,7 +370,8 @@ func (e *HTTPADCExecutor) buildHTTPRequest(ctx context.Context, serverAddr strin
Server: strings.Split(serverAddr, ","),
Token: config.Token,
LabelSelector: labels,
IncludeResourceType: types,
IncludeResourceType: includeTypes,
ExcludeResourceType: excludeTypes,
TlsSkipVerify: ptr.To(!tlsVerify),
CacheKey: config.Name,
BypassCache: bypassCache,
Expand All @@ -383,7 +394,8 @@ func (e *HTTPADCExecutor) buildHTTPRequest(ctx context.Context, serverAddr strin
"cacheKey", config.Name,
"bypassCache", bypassCache,
"labelSelector", labels,
"includeResourceType", types,
"includeResourceType", includeTypes,
"excludeResourceType", excludeTypes,
"tlsSkipVerify", !tlsVerify,
)

Expand Down
67 changes: 66 additions & 1 deletion internal/adc/client/executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func TestHTTPADCExecutorBuildHTTPRequestBypassCache(t *testing.T) {
}

build := func(config adctypes.Config, path string) (ADCServerOpts, string) {
req, err := e.buildHTTPRequest(context.Background(), "http://apisix:9180", config, nil, nil,
req, err := e.buildHTTPRequest(context.Background(), "http://apisix:9180", config, nil, nil, nil,
&adctypes.Resources{}, http.MethodPut, path)
require.NoError(t, err)
body, err := io.ReadAll(req.Body)
Expand Down Expand Up @@ -265,3 +265,68 @@ func TestIsConfVersionRejection(t *testing.T) {
assert.True(t, isConfVersionRejection(rejection("routes_conf_version has moved backwards")),
"the field is what names the rejection, not the sentence")
}

func TestBuildADCExecuteArgs(t *testing.T) {
tests := []struct {
name string
filePath string
labels map[string]string
includeTypes []string
excludeTypes []string
wantContains []string
wantAbsent []string
}{
{
name: "no filters",
filePath: "/tmp/sync.json",
wantContains: []string{"sync", "-f", "/tmp/sync.json"},
wantAbsent: []string{"--label-selector", "--include-resource-type", "--exclude-resource-type"},
},
{
name: "with label selector",
filePath: "/tmp/sync.json",
labels: map[string]string{"app": "apisix"},
wantContains: []string{"--label-selector", "app=apisix"},
wantAbsent: []string{"--include-resource-type", "--exclude-resource-type"},
},
{
name: "with include types",
filePath: "/tmp/sync.json",
includeTypes: []string{"Consumer"},
wantContains: []string{"--include-resource-type", "Consumer"},
wantAbsent: []string{"--exclude-resource-type"},
},
{
name: "with exclude types",
filePath: "/tmp/sync.json",
excludeTypes: []string{"Consumer"},
wantContains: []string{"--exclude-resource-type", "Consumer"},
wantAbsent: []string{"--include-resource-type"},
},
{
name: "with multiple exclude types",
filePath: "/tmp/sync.json",
excludeTypes: []string{"Consumer", "ConsumerGroup"},
wantContains: []string{"--exclude-resource-type", "Consumer", "ConsumerGroup"},
},
{
name: "with include and exclude types",
filePath: "/tmp/sync.json",
includeTypes: []string{"Consumer"},
excludeTypes: []string{"ConsumerGroup"},
wantContains: []string{"--include-resource-type", "Consumer", "--exclude-resource-type", "ConsumerGroup"},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
args := BuildADCExecuteArgs(tt.filePath, tt.labels, tt.includeTypes, tt.excludeTypes)
for _, want := range tt.wantContains {
require.Contains(t, args, want)
}
for _, absent := range tt.wantAbsent {
require.NotContains(t, args, absent)
}
})
}
}
7 changes: 4 additions & 3 deletions internal/controller/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,10 @@ type LeaderElection struct {
}

type ProviderConfig struct {
Type ProviderType `json:"type" yaml:"type"`
SyncPeriod types.TimeDuration `json:"sync_period" yaml:"sync_period"`
InitSyncDelay types.TimeDuration `json:"init_sync_delay" yaml:"init_sync_delay"`
Type ProviderType `json:"type" yaml:"type"`
SyncPeriod types.TimeDuration `json:"sync_period" yaml:"sync_period"`
InitSyncDelay types.TimeDuration `json:"init_sync_delay" yaml:"init_sync_delay"`
ExcludeResourceType []string `json:"exclude_resource_type" yaml:"exclude_resource_type"`
}

type WebhookConfig struct {
Expand Down
1 change: 1 addition & 0 deletions internal/manager/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ func Run(ctx context.Context, logger logr.Logger) error {
SyncPeriod: config.ControllerConfig.ProviderConfig.SyncPeriod.Duration,
InitSyncDelay: config.ControllerConfig.ProviderConfig.InitSyncDelay.Duration,
ListenerPortMatchMode: config.ControllerConfig.ListenerPortMatchMode,
ExcludeResourceType: config.ControllerConfig.ProviderConfig.ExcludeResourceType,
}
provider, err := provider.New(providerType, logger, updater.Writer(), readier, providerOptions)
if err != nil {
Expand Down
9 changes: 5 additions & 4 deletions internal/provider/apisix/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,10 @@ func (d *apisixProvider) Delete(ctx context.Context, obj client.Object) error {
// on deleted gateway level resources
if len(resourceTypes) == 0 {
return d.client.Delete(ctx, adcclient.Task{
Key: nnk,
Name: nnk.String(),
Labels: labels,
Key: nnk,
Name: nnk.String(),
Labels: labels,
ExcludeResourceType: d.ExcludeResourceType,
})
}
defer d.syncNotify()
Expand Down Expand Up @@ -294,7 +295,7 @@ func (d *apisixProvider) Start(ctx context.Context) error {
}

func (d *apisixProvider) sync(ctx context.Context) error {
statusesMap, err := d.client.Sync(ctx)
statusesMap, err := d.client.Sync(ctx, d.ExcludeResourceType)
d.handleADCExecutionErrors(statusesMap)
return err
}
Expand Down
4 changes: 4 additions & 0 deletions internal/provider/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type Options struct {
DefaultBackendMode string
DefaultResolveEndpoints bool
ListenerPortMatchMode config.ListenerPortMatchMode
ExcludeResourceType []string
}

func (o *Options) ApplyToList(lo *Options) {
Expand All @@ -55,6 +56,9 @@ func (o *Options) ApplyToList(lo *Options) {
if o.ListenerPortMatchMode != "" {
lo.ListenerPortMatchMode = o.ListenerPortMatchMode
}
if len(o.ExcludeResourceType) > 0 {
lo.ExcludeResourceType = o.ExcludeResourceType
}
}

func (o *Options) ApplyOptions(opts []Option) *Options {
Expand Down
Loading