diff --git a/internal/runner/options.go b/internal/runner/options.go index b6dcff2..9bd4bd5 100644 --- a/internal/runner/options.go +++ b/internal/runner/options.go @@ -32,6 +32,7 @@ type Options struct { Providers goflags.StringSlice // Providers specifies what providers to fetch assets for. Id goflags.StringSlice // Id specifies what id's to fetch assets for. Services goflags.StringSlice // Services specifies what services to fetch assets for a provider. + ExcludeServices goflags.StringSlice // ExcludeServices specifies what services to exclude for a provider. ExtendedMetadata bool // ExtendedMetadata enables extended metadata for providers. ProviderConfig string // ProviderConfig is the location of the provider config file. DisableUpdateCheck bool // DisableUpdateCheck disable automatic update check @@ -85,6 +86,7 @@ func ParseOptions() *Options { flagSet.BoolVar(&options.IPAddress, "ip", false, "display only ips in results"), flagSet.BoolVar(&options.ExtendedMetadata, "extended-metadata", false, "enable extended metadata for providers"), flagSet.StringSliceVarP(&options.Services, "service", "s", nil, "query and display results from given service (comma-separated)) (default "+strings.Join(defaultServies, ",")+")", goflags.CommaSeparatedStringSliceOptions), + flagSet.StringSliceVarP(&options.ExcludeServices, "exclude-service", "es", nil, "exclude given services from query results (comma-separated)", goflags.CommaSeparatedStringSliceOptions), flagSet.BoolVarP(&options.ExcludePrivate, "exclude-private", "ep", false, "exclude private ips in cli output"), ) flagSet.CreateGroup("update", "Update", diff --git a/internal/runner/runner.go b/internal/runner/runner.go index a6b3986..11d5dca 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -68,6 +68,9 @@ func (r *Runner) Enumerate() { if len(services) > 0 { item["services"] = strings.Join(services, ",") } + if len(r.options.ExcludeServices) > 0 { + item["exclude_services"] = strings.Join(r.options.ExcludeServices, ",") + } if r.options.ExtendedMetadata { item["extended_metadata"] = "true" } diff --git a/pkg/providers/alibaba/alibaba.go b/pkg/providers/alibaba/alibaba.go index f104241..407fcf9 100644 --- a/pkg/providers/alibaba/alibaba.go +++ b/pkg/providers/alibaba/alibaba.go @@ -47,18 +47,24 @@ func New(options schema.OptionBlock) (*Provider, error) { supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } provider.services = services if services.Has("instance") { diff --git a/pkg/providers/arvancloud/arvancloud.go b/pkg/providers/arvancloud/arvancloud.go index 0b0aa07..55d7820 100644 --- a/pkg/providers/arvancloud/arvancloud.go +++ b/pkg/providers/arvancloud/arvancloud.go @@ -41,18 +41,24 @@ func New(options schema.OptionBlock) (*Provider, error) { } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } return &Provider{id: id, client: api, services: services}, nil } diff --git a/pkg/providers/aws/aws.go b/pkg/providers/aws/aws.go index 8d30630..1405605 100644 --- a/pkg/providers/aws/aws.go +++ b/pkg/providers/aws/aws.go @@ -88,7 +88,8 @@ func (p *ProviderOptions) ParseOptionBlock(block schema.OptionBlock) error { supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := block.GetMetadata("services"); ok { + ss, servicesSpecified := block.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} @@ -96,11 +97,16 @@ func (p *ProviderOptions) ParseOptionBlock(block schema.OptionBlock) error { } } // if no services provided from -service flag, includes all services - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := block.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } p.Services = services if extendedMetadata, ok := block.GetMetadata("extended_metadata"); ok { diff --git a/pkg/providers/azure/azure.go b/pkg/providers/azure/azure.go index 3821732..57ab2ed 100644 --- a/pkg/providers/azure/azure.go +++ b/pkg/providers/azure/azure.go @@ -49,18 +49,24 @@ func New(options schema.OptionBlock) (*Provider, error) { supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } provider := &Provider{ Credential: credential, // Track 2: use credential instead of authorizer diff --git a/pkg/providers/cloudflare/cloudflare.go b/pkg/providers/cloudflare/cloudflare.go index aa2f7a1..6ad20e2 100644 --- a/pkg/providers/cloudflare/cloudflare.go +++ b/pkg/providers/cloudflare/cloudflare.go @@ -30,18 +30,24 @@ func New(options schema.OptionBlock) (*Provider, error) { } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } // Parse extended metadata option extendedMetadata := false diff --git a/pkg/providers/custom/custom.go b/pkg/providers/custom/custom.go index 11b2206..d8edeb8 100644 --- a/pkg/providers/custom/custom.go +++ b/pkg/providers/custom/custom.go @@ -89,7 +89,8 @@ func (p *ProviderOptions) ParseOptionBlock(block schema.OptionBlock) error { supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := block.GetMetadata("services"); ok { + ss, servicesSpecified := block.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} @@ -97,11 +98,16 @@ func (p *ProviderOptions) ParseOptionBlock(block schema.OptionBlock) error { } } // if no services provided from -service flag, includes all services - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := block.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } np, err := networkpolicy.New(networkpolicy.DefaultOptions) if err != nil { diff --git a/pkg/providers/digitalocean/digitalocean.go b/pkg/providers/digitalocean/digitalocean.go index 364be84..2bb43a1 100644 --- a/pkg/providers/digitalocean/digitalocean.go +++ b/pkg/providers/digitalocean/digitalocean.go @@ -31,18 +31,24 @@ func New(options schema.OptionBlock) (*Provider, error) { supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } // Check for extended metadata option extendedMetadata := false diff --git a/pkg/providers/dnssimple/dnssimple.go b/pkg/providers/dnssimple/dnssimple.go index 285b183..c0fabe7 100644 --- a/pkg/providers/dnssimple/dnssimple.go +++ b/pkg/providers/dnssimple/dnssimple.go @@ -58,18 +58,24 @@ func New(options schema.OptionBlock) (*Provider, error) { supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } provider := &Provider{ id: id, diff --git a/pkg/providers/gcp/gcp.go b/pkg/providers/gcp/gcp.go index e44588c..26e8434 100644 --- a/pkg/providers/gcp/gcp.go +++ b/pkg/providers/gcp/gcp.go @@ -244,18 +244,24 @@ func newIndividualProvider(options schema.OptionBlock, id, JSONData string) (*Pr supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } provider.services = services configuredProjects := getProjectIDsFromOptions(options) @@ -663,7 +669,8 @@ func newOrganizationProvider(options schema.OptionBlock, id, JSONData, organizat } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} diff --git a/pkg/providers/heroku/heroku.go b/pkg/providers/heroku/heroku.go index ac2a59d..fba74e8 100644 --- a/pkg/providers/heroku/heroku.go +++ b/pkg/providers/heroku/heroku.go @@ -38,18 +38,24 @@ func New(options schema.OptionBlock) (*Provider, error) { supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } return &Provider{id: id, client: heroku.NewService(heroku.DefaultClient), services: services}, nil } diff --git a/pkg/providers/hetzner/hetzner.go b/pkg/providers/hetzner/hetzner.go index 25ff5f0..26b107a 100644 --- a/pkg/providers/hetzner/hetzner.go +++ b/pkg/providers/hetzner/hetzner.go @@ -37,18 +37,24 @@ func New(options schema.OptionBlock) (*Provider, error) { supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } return &Provider{id: id, client: hetzner.NewClient(opts), services: services}, nil } diff --git a/pkg/providers/k8s/kubernetes.go b/pkg/providers/k8s/kubernetes.go index 659fd7d..ca30881 100644 --- a/pkg/providers/k8s/kubernetes.go +++ b/pkg/providers/k8s/kubernetes.go @@ -69,18 +69,24 @@ func New(options schema.OptionBlock) (*Provider, error) { supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } var providerExtendedMetadata bool if extendedMetadata, ok := options.GetMetadata("extended_metadata"); ok { providerExtendedMetadata = extendedMetadata == "true" @@ -106,11 +112,11 @@ func (p *Provider) Services() []string { // Resources returns the provider for an resource deployment source. func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { finalList := schema.NewResources() - services, err := p.clientSet.CoreV1().Services("").List(ctx, metav1.ListOptions{}) - if err != nil { - return nil, errkit.Wrap(err, "could not list kubernetes services") - } if p.services.Has("service") { + services, err := p.clientSet.CoreV1().Services("").List(ctx, metav1.ListOptions{}) + if err != nil { + return nil, errkit.Wrap(err, "could not list kubernetes services") + } k8sServiceProvider := K8sServiceProvider{serviceClient: services, id: p.id} serviceIPs, _ := k8sServiceProvider.GetResource(ctx) finalList.Merge(serviceIPs) diff --git a/pkg/providers/linode/linode.go b/pkg/providers/linode/linode.go index 389fa11..3b8a0f7 100644 --- a/pkg/providers/linode/linode.go +++ b/pkg/providers/linode/linode.go @@ -47,18 +47,24 @@ func New(options schema.OptionBlock) (*Provider, error) { supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } return &Provider{id: id, client: &client, services: services}, nil } diff --git a/pkg/providers/namecheap/namecheap.go b/pkg/providers/namecheap/namecheap.go index d1d86b8..ad15f07 100644 --- a/pkg/providers/namecheap/namecheap.go +++ b/pkg/providers/namecheap/namecheap.go @@ -57,18 +57,24 @@ func New(options schema.OptionBlock) (*Provider, error) { supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } return &Provider{id: id, client: namecheap.NewClient(&clientOptions), services: services}, nil } diff --git a/pkg/providers/openstack/openstack.go b/pkg/providers/openstack/openstack.go index f836d53..2cb0baf 100644 --- a/pkg/providers/openstack/openstack.go +++ b/pkg/providers/openstack/openstack.go @@ -87,18 +87,24 @@ func New(options schema.OptionBlock) (*Provider, error) { supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } return &Provider{id: id, client: client, services: services}, nil } diff --git a/pkg/providers/ovh/ovh.go b/pkg/providers/ovh/ovh.go index 1fcb3da..a934c75 100644 --- a/pkg/providers/ovh/ovh.go +++ b/pkg/providers/ovh/ovh.go @@ -26,7 +26,8 @@ func New(options schema.OptionBlock) (*Provider, error) { // service selection supported := map[string]struct{}{"dns": {}} services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { s = strings.TrimSpace(s) if _, ok := supported[s]; ok { @@ -34,11 +35,16 @@ func New(options schema.OptionBlock) (*Provider, error) { } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } // OVH endpoint (default ovh-eu) endpoint := "ovh-eu" diff --git a/pkg/providers/scaleway/scaleway.go b/pkg/providers/scaleway/scaleway.go index 6277b52..4775cd1 100644 --- a/pkg/providers/scaleway/scaleway.go +++ b/pkg/providers/scaleway/scaleway.go @@ -34,18 +34,24 @@ func New(options schema.OptionBlock) (*Provider, error) { supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } client, err := scw.NewClient(scw.WithAuth(accessKey, accessToken)) if err != nil { diff --git a/pkg/providers/terraform/terraform.go b/pkg/providers/terraform/terraform.go index 9adcb09..82ca678 100644 --- a/pkg/providers/terraform/terraform.go +++ b/pkg/providers/terraform/terraform.go @@ -34,18 +34,24 @@ func New(options schema.OptionBlock) (*Provider, error) { supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } return &Provider{path: StatePathFile, id: id, services: services}, nil } diff --git a/pkg/providers/vercel/vercel.go b/pkg/providers/vercel/vercel.go index 48aecd4..2754042 100644 --- a/pkg/providers/vercel/vercel.go +++ b/pkg/providers/vercel/vercel.go @@ -30,18 +30,24 @@ func New(options schema.OptionBlock) (*Provider, error) { supportedServicesMap[s] = struct{}{} } services := make(schema.ServiceMap) - if ss, ok := options.GetMetadata("services"); ok { + ss, servicesSpecified := options.GetMetadata("services") + if servicesSpecified { for _, s := range strings.Split(ss, ",") { if _, ok := supportedServicesMap[s]; ok { services[s] = struct{}{} } } } - if len(services) == 0 { + if !servicesSpecified { for _, s := range Services { services[s] = struct{}{} } } + if es, ok := options.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + delete(services, strings.TrimSpace(s)) + } + } client := newAPIClient(newClientConfig{ Token: accessKey, diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go index 67ad620..65f7226 100644 --- a/pkg/schema/schema.go +++ b/pkg/schema/schema.go @@ -226,6 +226,22 @@ func (o Options) GetServiceNames() []string { return services } +// GetExcludeServiceNames returns the excluded services from the options +func (o Options) GetExcludeServiceNames() []string { + services := make([]string, 0) + for _, option := range o { + if serviceNameList, ok := option["exclude_services"]; ok { + for _, serviceName := range strings.Split(serviceNameList, ",") { + trimmedServiceName := strings.TrimSpace(serviceName) + if trimmedServiceName != "" { + services = append(services, trimmedServiceName) + } + } + } + } + return services +} + // OptionBlock is a single option on which operation is possible type OptionBlock map[string]string @@ -240,7 +256,7 @@ func (ob *OptionBlock) UnmarshalYAML(unmarshal func(interface{}) error) error { // Convert raw map to OptionBlock and handle special cases for key, value := range rawMap { switch key { - case "account_ids", "exclude_account_ids", "urls", "services", "project_ids", "exclude_project_ids": + case "account_ids", "exclude_account_ids", "urls", "services", "exclude_services", "project_ids", "exclude_project_ids": if valueArr, ok := value.([]interface{}); ok { var strArr []string for _, v := range valueArr { @@ -296,6 +312,42 @@ func (o OptionBlock) GetMetadata(key string) (string, bool) { return data, true } +// ParseServices parses services and exclude_services metadata from option block against supported services +func (o OptionBlock) ParseServices(supportedServices []string) ServiceMap { + supportedServicesMap := make(map[string]struct{}) + for _, s := range supportedServices { + supportedServicesMap[s] = struct{}{} + } + + services := make(ServiceMap) + ss, servicesSpecified := o.GetMetadata("services") + if servicesSpecified { + for _, s := range strings.Split(ss, ",") { + s = strings.TrimSpace(s) + if _, ok := supportedServicesMap[s]; ok { + services[s] = struct{}{} + } + } + } + + // if no services explicitly specified, start with all supported services + if !servicesSpecified { + for _, s := range supportedServices { + services[s] = struct{}{} + } + } + + // subtract exclude_services if specified + if es, ok := o.GetMetadata("exclude_services"); ok { + for _, s := range strings.Split(es, ",") { + s = strings.TrimSpace(s) + delete(services, s) + } + } + + return services +} + type ServiceMap map[string]struct{} func (s ServiceMap) Has(service string) bool { diff --git a/pkg/schema/schema_test.go b/pkg/schema/schema_test.go index 86d91c5..c50e8b6 100644 --- a/pkg/schema/schema_test.go +++ b/pkg/schema/schema_test.go @@ -89,3 +89,45 @@ func TestOptionBlockScalarFallback(t *testing.T) { require.True(t, ok) require.Equal(t, "PDScannerRole", value) } + +func TestOptionBlockParsesExcludeServices(t *testing.T) { + data := ` +- provider: gcp + exclude_services: + - cloud-function + - cloud-run +` + var options Options + err := yaml.Unmarshal([]byte(data), &options) + require.NoError(t, err) + require.Len(t, options, 1) + + value, ok := options[0].GetMetadata("exclude_services") + require.True(t, ok) + require.Equal(t, "cloud-function,cloud-run", value) + require.Equal(t, []string{"cloud-function", "cloud-run"}, options.GetExcludeServiceNames()) + + supported := []string{"dns", "compute", "gke", "cloud-function", "cloud-run"} + serviceMap := options[0].ParseServices(supported) + require.True(t, serviceMap.Has("dns")) + require.True(t, serviceMap.Has("compute")) + require.True(t, serviceMap.Has("gke")) + require.False(t, serviceMap.Has("cloud-function")) + require.False(t, serviceMap.Has("cloud-run")) +} + +func TestOptionBlockExplicitUnsupportedServicesDoesNotFallback(t *testing.T) { + data := ` +- provider: gcp + services: + - unknown +` + var options Options + err := yaml.Unmarshal([]byte(data), &options) + require.NoError(t, err) + require.Len(t, options, 1) + + supported := []string{"dns", "compute"} + serviceMap := options[0].ParseServices(supported) + require.Equal(t, 0, len(serviceMap)) +}