From b11f8d989a0bbf113b9a0280f91855cf31d7600b Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Tue, 5 Aug 2025 08:44:01 -0500 Subject: [PATCH 01/32] feat(cli): release check registered components: convo + partial state Signed-off-by: Samantha Coyle --- .build-tools/README.md | 20 + .../cmd/cmd-check-component-registrations.go | 402 ++++++++++++++++++ .build-tools/cmd/cmd-gen-component-schema.go | 2 +- 3 files changed, 423 insertions(+), 1 deletion(-) create mode 100644 .build-tools/cmd/cmd-check-component-registrations.go diff --git a/.build-tools/README.md b/.build-tools/README.md index b760464fc6..ea6c484357 100644 --- a/.build-tools/README.md +++ b/.build-tools/README.md @@ -16,3 +16,23 @@ You have two ways to run the CLI: The list of available commands in this CLI is dynamic and is subject to change at any time. Each command, including the "root" one (no sub-command), are self-documented in the CLI, and you can read the help page by adding `--help`. For example, `./build-tools --help` shows the full list of commands the CLI offers. + +### check-component-registrations + +Checks that all components in components-contrib are properly registered in dapr/dapr. This includes checking for: + +- Registry files in dapr/pkg/components/ +- Registration of specific components in dapr/cmd/daprd/components/ +- Metadata files in component directories + +Usage: +```bash +# Run from build-tools directory +go run . check-component-registrations + +# Or using the compiled binary +./build-tools check-component-registrations +``` + +This command will scan all component types (conversation, state, secretstores, pubsub, bindings, configuration) and report any missing registrations. +This is part of the release endgame tasking to ensure all components properly register within runtime as expected. diff --git a/.build-tools/cmd/cmd-check-component-registrations.go b/.build-tools/cmd/cmd-check-component-registrations.go new file mode 100644 index 0000000000..a5e2e7002b --- /dev/null +++ b/.build-tools/cmd/cmd-check-component-registrations.go @@ -0,0 +1,402 @@ +/* +Copyright 2025 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "fmt" + "os/exec" + "strings" + + "github.com/spf13/cobra" +) + +// checkComponentRegistrationsCmd represents the check-component-registrations command +// go run . check-component-registrations +// This automates an endgame task that must be completed before a release. +// It checks that all components in components-contrib are properly registered in dapr/dapr. +var checkComponentRegistrationsCmd = &cobra.Command{ + Use: "check-component-registrations", + Short: "Checks that all components are properly registered", + Long: `Checks that all components in components-contrib are properly registered in dapr/dapr. +This includes checking for: +- Registry files in dapr/pkg/components/ +- Registration of specific components in dapr/cmd/daprd/components/ +- Metadata files in component directories + +This is a required step before an official Dapr release.`, + Run: func(cmd *cobra.Command, args []string) { + // Navigate to the root of the repo + err := cwdToRepoRoot() + if err != nil { + panic(err) + } + + fmt.Println("Checking Dapr Component Registrations across runtime in dapr/dapr and components-contrib") + fmt.Println("========================================================================================") + + checkConversationComponents() + checkStateComponents() + + // TODO: secretstore, pubsub, binding, config + + fmt.Println("\nCheck completed!") + }, +} + +func init() { + rootCmd.AddCommand(checkComponentRegistrationsCmd) +} + +func checkConversationComponents() { + fmt.Println("\nChecking conversation components...") + checkComponents("conversation") +} + +func checkStateComponents() { + fmt.Println("\nChecking state components...") + + // ignoreDaprComponents := []string{"yugabyte, yugabytedb"} + checkComponents("state") +} + +// Note: because this cli cmd changes to the working directory to the root of the repo so pathing is relative to that. +func checkComponents(componentType string) { + if err := checkRegistry(componentType); err != nil { + fmt.Printf("Registry check failed: %v\n", err) + return + } + + contribComponents, daprComponents, err := findComponentsInBothRepos(componentType) + if err != nil { + fmt.Printf("Component discovery across repos failed: %v\n", err) + return + } + + // Apply vendor prefix mapping and deduplication to both lists. + // This removes things like the CSP prefixing. + mappedContribComponents := mapAndDeduplicateComponents(contribComponents) + mappedDaprComponents := mapAndDeduplicateComponents(daprComponents) + + fmt.Printf("Components in contrib: %d\n", len(mappedContribComponents)) + fmt.Printf("Components registered: %d\n", len(mappedDaprComponents)) + + if len(mappedContribComponents) != len(mappedDaprComponents) { + fmt.Printf("Number of components in contrib and dapr/dapr do not match") + fmt.Printf("Contrib: %v\n", mappedContribComponents) + fmt.Printf("Dapr: %v\n", mappedDaprComponents) + return + } + + missing, err := validateComponents(contribComponents, componentType) + if err != nil { + fmt.Printf("Component validation failed: %v\n", err) + return + } + + reportResults(missing, componentType) +} + +func checkRegistry(componentType string) error { + // Check for default registry singleton + registryCmd := exec.Command("grep", "-r", `var DefaultRegistry \*Registry = NewRegistry\(\)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) + _, err := registryCmd.Output() + if err != nil { + return fmt.Errorf("could not find default registry: %v", err) + } + + // Check for RegisterComponent() + registerComponentCmd := exec.Command("grep", "-r", `Registry) RegisterComponent(componentFactory func(logger.Logger)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) + _, err = registerComponentCmd.Output() + if err != nil { + return fmt.Errorf("could not find RegisterComponent method: %v", err) + } + + // Check for Create() + createCmd := exec.Command("grep", "-r", `Registry) Create(name, version, logName string)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) + _, err = createCmd.Output() + if err != nil { + return fmt.Errorf("could not find Create method: %v", err) + } + + return nil +} + +func findComponentsInBothRepos(componentType string) ([]string, []string, error) { + // Find all components in components-contrib + contribCmd := exec.Command("grep", "-rl", "--include=*.go", "func New", componentType) + contribOutput, err := contribCmd.Output() + if err != nil { + return nil, nil, fmt.Errorf("could not find components within components-contrib: %v", err) + } + + // Find all registered components in dapr/dapr + registeredCmd := exec.Command("sh", "-c", fmt.Sprintf(`grep -r "RegisterComponent" ../dapr/cmd/daprd/components/%s_*.go`, componentType)) + registeredOutput, err := registeredCmd.Output() + if err != nil { + return nil, nil, fmt.Errorf("could not find all registered components in dapr/dapr: %v", err) + } + + contribComponents := parseContribComponents(string(contribOutput), componentType) + registeredComponents := parseRegisteredComponents(string(registeredOutput)) + + return contribComponents, registeredComponents, nil +} + +func validateComponents(contribComponents []string, componentType string) ([]string, error) { + missing := []string{} + + for _, contrib := range contribComponents { + if err := validateComponent(contrib, componentType); err != nil { + missing = append(missing, contrib) + } + } + + return missing, nil +} + +func validateComponent(contrib, componentType string) error { + if err := checkComponentRegistration(contrib, componentType); err != nil { + return err + } + + if err := checkBuildTag(contrib, componentType); err != nil { + return err + } + + if err := checkMetadataFile(contrib, componentType); err != nil { + return err + } + + return nil +} + +func checkComponentRegistration(contrib, componentType string) error { + // For registration files, use the normalized component name + // EX: aws.bedrock -> conversation_bedrock.go + compName := normalizeComponentName(contrib) + compFileName := fmt.Sprintf("../dapr/cmd/daprd/components/%s_%s.go", componentType, compName) + + fileExistsCmd := exec.Command("ls", compFileName) + _, err := fileExistsCmd.Output() + if err != nil { + return fmt.Errorf("registration file for '%s' not found: %s", contrib, compFileName) + } + + if err := checkComponentIsActuallyRegisteredInFile(contrib, compFileName); err != nil { + return fmt.Errorf("component '%s' not registered in %s: %v", contrib, compFileName, err) + } + + fmt.Printf("Component '%s' properly registered in %s\n", contrib, compFileName) + return nil +} + +// checkComponentIsActuallyRegisteredInFile basically checks for if the component name string is within the file +// to ensure it is properly registered within runtime. +func checkComponentIsActuallyRegisteredInFile(contrib, registrationFile string) error { + registrationCheckCmd := exec.Command("grep", "-r", `"`, registrationFile) + registrationOutput, err := registrationCheckCmd.Output() + if err != nil { + return fmt.Errorf("could not read registration file: %v", err) + } + + lines := strings.FieldsFunc(string(registrationOutput), func(r rune) bool { + return r == '\n' + }) + + for _, line := range lines { + if strings.Contains(line, "RegisterComponent") { + quoteIndex := strings.Index(line, "\"") + if quoteIndex > 0 { + rest := line[quoteIndex+1:] + endQuoteIndex := strings.Index(rest, "\"") + if endQuoteIndex > 0 { + registeredName := rest[:endQuoteIndex] + // Check exact match + if registeredName == contrib { + return nil + } + // Check vendor prefix mapping (e.g., hashicorp.consul -> consul) + normalizedName := normalizeComponentName(contrib) + if registeredName == normalizedName { + return nil + } + } + } + } + } + + return fmt.Errorf("component not found in registration file") +} + +func checkBuildTag(contrib, componentType string) error { + compName := normalizeComponentName(contrib) + compFileName := fmt.Sprintf("../dapr/cmd/daprd/components/%s_%s.go", componentType, compName) + + buildTagCmd := exec.Command("grep", "-q", "go:build allcomponents", compFileName) + _, err := buildTagCmd.Output() + if err != nil { + return fmt.Errorf("build tag 'go:build allcomponents' not found in %s", compFileName) + } + + fmt.Printf("Build tag found in %s\n", compFileName) + return nil +} + +// normalizeComponentName strips vendor prefixes and versions from component names +// EX: aws.bedrock -> bedrock +// EX: postgresql.v1 -> postgresql +// EX: hashicorp.consul -> consul +func normalizeComponentName(contrib string) string { + if strings.Contains(contrib, ".") { + parts := strings.Split(contrib, ".") + if len(parts) == 2 { + vendorPrefixes := []string{"hashicorp", "aws", "azure", "gcp", "alicloud", "oci", "cloudflare"} + for _, prefix := range vendorPrefixes { + if parts[0] == prefix { + return parts[1] + } + } + // Strip version suffixes + if parts[1] == "v1" || parts[1] == "v2" { + return parts[0] + } + } + } + return contrib +} + +func mapAndDeduplicateComponents(components []string) []string { + mappedComponents := make([]string, 0, len(components)) + seen := make(map[string]bool) + + for _, component := range components { + simpleName := normalizeComponentName(component) + if !seen[simpleName] { + mappedComponents = append(mappedComponents, simpleName) + seen[simpleName] = true + } + } + + return mappedComponents +} + +func checkMetadataFile(contrib, componentType string) error { + metadataFile := getMetadataFilePath(contrib, componentType) + + metadataExistsCmd := exec.Command("ls", metadataFile) + _, err := metadataExistsCmd.Output() + if err != nil { + return fmt.Errorf("metadata file for '%s' not found: %s", contrib, metadataFile) + } + + fmt.Printf("Metadata file for '%s' found: %s\n", contrib, metadataFile) + return nil +} + +func getMetadataFilePath(contrib, componentType string) string { + if strings.Contains(contrib, ".") { + // For nested components like "aws.bedrock", split and join with "/" + parts := strings.Split(contrib, ".") + return fmt.Sprintf("%s/%s/metadata.yaml", componentType, strings.Join(parts, "/")) + } + // For simple components like "echo" + return fmt.Sprintf("%s/%s/metadata.yaml", componentType, contrib) +} + +func reportResults(missing []string, componentType string) { + if len(missing) > 0 { + fmt.Printf("Missing registrations: %d\n", len(missing)) + for _, m := range missing { + fmt.Printf(" - %s\n", m) + } + } else { + fmt.Printf("All %s components are registered\n", componentType) + } +} + +func parseContribComponents(output, componentType string) []string { + components := []string{} + lines := strings.FieldsFunc(output, func(r rune) bool { + return r == '\n' + }) + + for _, line := range lines { + line = strings.TrimSpace(line) + if line == "" { + continue + } + + componentName := extractComponentNameFromPath(line, componentType) + if componentName != "" { + components = append(components, componentName) + } + } + + return components +} + +// extractComponentNameFromPath extracts a component name from file path +// EX: conversation/echo/echo.go -> echo +// EX: conversation/aws/bedrock/bedrock.go -> aws.bedrock +func extractComponentNameFromPath(filePath, componentType string) string { + // Find the component type directory in the path + componentTypeIndex := strings.Index(filePath, componentType+"/") + if componentTypeIndex < 0 { + return "" + } + + // Extract the path after componentType/ + pathAfterComponentType := filePath[componentTypeIndex+len(componentType+"/"):] + pathWithoutExt := strings.TrimSuffix(pathAfterComponentType, ".go") + + // Convert directory structure to component name + // Replace slashes with dots, but only up to the last directory + parts := strings.Split(pathWithoutExt, "/") + if len(parts) >= 2 { + // For nested components: aws/bedrock/bedrock.go -> aws.bedrock + // Take all parts except the last (which is the filename) + dirParts := parts[:len(parts)-1] + return strings.Join(dirParts, ".") + } else if len(parts) == 1 { + // For simple components: echo/echo.go -> echo + // The part before the slash is the component name + return parts[0] + } + + return "" +} + +// parseRegisteredComponents parses the output of the grep command to find all registered components +func parseRegisteredComponents(output string) []string { + components := []string{} + lines := strings.FieldsFunc(output, func(r rune) bool { + return r == '\n' + }) + + for _, line := range lines { + if strings.Contains(line, "RegisterComponent") { + quoteIndex := strings.Index(line, "\"") + if quoteIndex > 0 { + rest := line[quoteIndex+1:] + endQuoteIndex := strings.Index(rest, "\"") + if endQuoteIndex > 0 { + componentName := rest[:endQuoteIndex] + components = append(components, componentName) + } + } + } + } + + return components +} diff --git a/.build-tools/cmd/cmd-gen-component-schema.go b/.build-tools/cmd/cmd-gen-component-schema.go index 7be0c8005f..3199670ef5 100644 --- a/.build-tools/cmd/cmd-gen-component-schema.go +++ b/.build-tools/cmd/cmd-gen-component-schema.go @@ -39,7 +39,7 @@ The result is written to stdout.`, } res := reflector.Reflect(&metadataschema.ComponentMetadata{}) res.Title = "ComponentMetadata" - // Print resut to stdout + // Print result to stdout enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") _ = enc.Encode(res) From ab0619d8ffa6e309ec7c8a29eb932537164bc833 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Tue, 5 Aug 2025 16:02:13 -0500 Subject: [PATCH 02/32] fix: add state components working properly with cli Signed-off-by: Samantha Coyle --- .../cmd/cmd-check-component-registrations.go | 218 +++++++++++++----- conversation/echo/metadata.yaml | 11 + state/aerospike/aerospike.go | 6 +- state/aerospike/metadata.yaml | 26 +++ state/alicloud/tablestore/metadata.yaml | 36 +++ state/couchbase/couchbase.go | 12 +- state/couchbase/metadata.yaml | 43 ++++ state/etcd/etcd.go | 2 +- state/etcd/metadata.yaml | 69 ++++++ state/gcp/firestore/firestore.go | 5 +- state/gcp/firestore/metadata.yaml | 83 +++++++ state/hashicorp/consul/metadata.yaml | 36 +++ state/hazelcast/hazelcast.go | 4 +- state/hazelcast/metadata.yaml | 21 ++ state/jetstream/metadata.yaml | 37 +++ state/oci/objectstorage/metadata.yaml | 79 +++++++ state/oci/objectstorage/objectstorage.go | 9 +- state/oracledatabase/metadata.yaml | 31 +++ state/oracledatabase/oracledatabaseaccess.go | 6 +- state/rethinkdb/metadata.yaml | 81 +++++++ state/rethinkdb/rethinkdb.go | 9 +- state/sqlite/metadata.yaml | 53 +++++ state/zookeeper/metadata.yaml | 39 ++++ state/zookeeper/zk.go | 1 + 24 files changed, 834 insertions(+), 83 deletions(-) create mode 100644 conversation/echo/metadata.yaml create mode 100644 state/aerospike/metadata.yaml create mode 100644 state/alicloud/tablestore/metadata.yaml create mode 100644 state/couchbase/metadata.yaml create mode 100644 state/etcd/metadata.yaml create mode 100644 state/gcp/firestore/metadata.yaml create mode 100644 state/hashicorp/consul/metadata.yaml create mode 100644 state/hazelcast/metadata.yaml create mode 100644 state/jetstream/metadata.yaml create mode 100644 state/oci/objectstorage/metadata.yaml create mode 100644 state/oracledatabase/metadata.yaml create mode 100644 state/rethinkdb/metadata.yaml create mode 100644 state/sqlite/metadata.yaml create mode 100644 state/zookeeper/metadata.yaml diff --git a/.build-tools/cmd/cmd-check-component-registrations.go b/.build-tools/cmd/cmd-check-component-registrations.go index a5e2e7002b..9b92488b9e 100644 --- a/.build-tools/cmd/cmd-check-component-registrations.go +++ b/.build-tools/cmd/cmd-check-component-registrations.go @@ -16,6 +16,7 @@ package cmd import ( "fmt" "os/exec" + "slices" "strings" "github.com/spf13/cobra" @@ -60,29 +61,46 @@ func init() { func checkConversationComponents() { fmt.Println("\nChecking conversation components...") - checkComponents("conversation") + checkComponents("conversation", []string{}, []string{}) } func checkStateComponents() { fmt.Println("\nChecking state components...") - // ignoreDaprComponents := []string{"yugabyte, yugabytedb"} - checkComponents("state") + // Yugabyte are supported via the postgres component, so in contrib this is covered by the postgresql component in the contrib list. + // Also, postgres = postgresql, so we can ignore postgres. + ignoreDaprComponents := []string{"yugabyte", "yugabytedb", "postgres"} + ignoreContribComponents := []string{"azure.blobstorage.internal"} + checkComponents("state", ignoreDaprComponents, ignoreContribComponents) } // Note: because this cli cmd changes to the working directory to the root of the repo so pathing is relative to that. -func checkComponents(componentType string) { +func checkComponents(componentType string, ignoreDaprComponents []string, ignoreContribComponents []string) { if err := checkRegistry(componentType); err != nil { fmt.Printf("Registry check failed: %v\n", err) return } - contribComponents, daprComponents, err := findComponentsInBothRepos(componentType) + contribComponents, daprComponents, err := findComponentsInBothRepos(componentType, ignoreContribComponents) if err != nil { fmt.Printf("Component discovery across repos failed: %v\n", err) return } + fmt.Printf("Components to ignore: %v\n", ignoreDaprComponents) + fmt.Printf("Dapr components before filtering: %v\n", daprComponents) + + // Filter out components to ignore + filteredDaprComponents := make([]string, 0, len(daprComponents)) + for _, comp := range daprComponents { + if !slices.Contains(ignoreDaprComponents, comp) { + filteredDaprComponents = append(filteredDaprComponents, comp) + } else { + fmt.Printf("Ignoring component: %s\n", comp) + } + } + daprComponents = filteredDaprComponents + // Apply vendor prefix mapping and deduplication to both lists. // This removes things like the CSP prefixing. mappedContribComponents := mapAndDeduplicateComponents(contribComponents) @@ -92,19 +110,19 @@ func checkComponents(componentType string) { fmt.Printf("Components registered: %d\n", len(mappedDaprComponents)) if len(mappedContribComponents) != len(mappedDaprComponents) { - fmt.Printf("Number of components in contrib and dapr/dapr do not match") + fmt.Printf("\nNumber of components in contrib and dapr/dapr do not match") fmt.Printf("Contrib: %v\n", mappedContribComponents) fmt.Printf("Dapr: %v\n", mappedDaprComponents) return } - missing, err := validateComponents(contribComponents, componentType) + missingRegistrations, missingBuildTags, missingMetadata, err := validateComponents(contribComponents, componentType) if err != nil { fmt.Printf("Component validation failed: %v\n", err) return } - reportResults(missing, componentType) + reportResults(missingRegistrations, missingBuildTags, missingMetadata, componentType) } func checkRegistry(componentType string) error { @@ -132,9 +150,9 @@ func checkRegistry(componentType string) error { return nil } -func findComponentsInBothRepos(componentType string) ([]string, []string, error) { - // Find all components in components-contrib - contribCmd := exec.Command("grep", "-rl", "--include=*.go", "func New", componentType) +func findComponentsInBothRepos(componentType string, ignoreContribComponents []string) ([]string, []string, error) { + // Find all components in components-contrib, excluding utility files + contribCmd := exec.Command("grep", "-rl", "--include=*.go", "--exclude=errors.go", "--exclude=bulk.go", "--exclude=query.go", "func New", componentType) contribOutput, err := contribCmd.Output() if err != nil { return nil, nil, fmt.Errorf("could not find components within components-contrib: %v", err) @@ -147,45 +165,42 @@ func findComponentsInBothRepos(componentType string) ([]string, []string, error) return nil, nil, fmt.Errorf("could not find all registered components in dapr/dapr: %v", err) } - contribComponents := parseContribComponents(string(contribOutput), componentType) + contribComponents := parseContribComponents(string(contribOutput), componentType, ignoreContribComponents) registeredComponents := parseRegisteredComponents(string(registeredOutput)) return contribComponents, registeredComponents, nil } -func validateComponents(contribComponents []string, componentType string) ([]string, error) { - missing := []string{} +func validateComponents(contribComponents []string, componentType string) ([]string, []string, []string, error) { + missingRegistrations := []string{} + missingBuildTags := []string{} + missingMetadata := []string{} for _, contrib := range contribComponents { - if err := validateComponent(contrib, componentType); err != nil { - missing = append(missing, contrib) - } - } - - return missing, nil -} - -func validateComponent(contrib, componentType string) error { - if err := checkComponentRegistration(contrib, componentType); err != nil { - return err - } - - if err := checkBuildTag(contrib, componentType); err != nil { - return err - } + registrationErr := checkComponentRegistration(contrib, componentType) + buildTagErr := checkBuildTag(contrib, componentType) + metadataErr := checkMetadataFile(contrib, componentType) - if err := checkMetadataFile(contrib, componentType); err != nil { - return err + if registrationErr != nil { + missingRegistrations = append(missingRegistrations, contrib) + } + if buildTagErr != nil { + missingBuildTags = append(missingBuildTags, contrib) + } + if metadataErr != nil { + missingMetadata = append(missingMetadata, contrib) + } } - return nil + return missingRegistrations, missingBuildTags, missingMetadata, nil } func checkComponentRegistration(contrib, componentType string) error { - // For registration files, use the normalized component name + // For registration files, convert component name to file naming convention // EX: aws.bedrock -> conversation_bedrock.go - compName := normalizeComponentName(contrib) - compFileName := fmt.Sprintf("../dapr/cmd/daprd/components/%s_%s.go", componentType, compName) + // EX: alicloud.tablestore -> state_alicloud_tablestore.go + compFileName := getRegistrationFileName(contrib, componentType) + fmt.Printf("sam the comp file name: %s\n", compFileName) fileExistsCmd := exec.Command("ls", compFileName) _, err := fileExistsCmd.Output() @@ -228,7 +243,9 @@ func checkComponentIsActuallyRegisteredInFile(contrib, registrationFile string) } // Check vendor prefix mapping (e.g., hashicorp.consul -> consul) normalizedName := normalizeComponentName(contrib) - if registeredName == normalizedName { + fmt.Printf("sam the normalized name: %s with registered name: %s\n", normalizedName, registeredName) + // registeredName can be something like azure.blobstorage, so need to see if contains too + if registeredName == normalizedName || strings.Contains(registeredName, normalizedName) { return nil } } @@ -236,12 +253,11 @@ func checkComponentIsActuallyRegisteredInFile(contrib, registrationFile string) } } - return fmt.Errorf("component not found in registration file") + return fmt.Errorf("component '%s' not found in registration file '%s'", contrib, registrationFile) } func checkBuildTag(contrib, componentType string) error { - compName := normalizeComponentName(contrib) - compFileName := fmt.Sprintf("../dapr/cmd/daprd/components/%s_%s.go", componentType, compName) + compFileName := getRegistrationFileName(contrib, componentType) buildTagCmd := exec.Command("grep", "-q", "go:build allcomponents", compFileName) _, err := buildTagCmd.Output() @@ -256,23 +272,48 @@ func checkBuildTag(contrib, componentType string) error { // normalizeComponentName strips vendor prefixes and versions from component names // EX: aws.bedrock -> bedrock // EX: postgresql.v1 -> postgresql +// EX: azure.blobstorage.v1 -> blobstorage // EX: hashicorp.consul -> consul func normalizeComponentName(contrib string) string { - if strings.Contains(contrib, ".") { - parts := strings.Split(contrib, ".") - if len(parts) == 2 { - vendorPrefixes := []string{"hashicorp", "aws", "azure", "gcp", "alicloud", "oci", "cloudflare"} - for _, prefix := range vendorPrefixes { - if parts[0] == prefix { - return parts[1] - } - } - // Strip version suffixes - if parts[1] == "v1" || parts[1] == "v2" { - return parts[0] + if !strings.Contains(contrib, ".") { + return contrib + } + + parts := strings.Split(contrib, ".") + vendorPrefixes := []string{"hashicorp", "aws", "azure", "gcp", "alicloud", "oci", "cloudflare"} + versionSuffixes := []string{"v1", "v2", "internal"} + + // Handle 2-part names (vendor.component) + if len(parts) == 2 { + // Strip vendor prefixes + if slices.Contains(vendorPrefixes, parts[0]) { + return parts[1] + } + // Strip version suffixes + if slices.Contains(versionSuffixes, parts[1]) { + return parts[0] + } + } + + // Handle 3+ part names (vendor.component.version) + if len(parts) >= 3 { + // Check if first part is a vendor prefix + if slices.Contains(vendorPrefixes, parts[0]) { + // Check if last part is a version suffix + if slices.Contains(versionSuffixes, parts[len(parts)-1]) { + // Return middle parts: azure.blobstorage.v1 -> blobstorage + return strings.Join(parts[1:len(parts)-1], ".") } + // Return all parts except vendor: azure.cosmosdb -> cosmosdb + return strings.Join(parts[1:], ".") + } + // Check if last part is a version suffix + if slices.Contains(versionSuffixes, parts[len(parts)-1]) { + // Return all parts except version: component.v1 -> component + return strings.Join(parts[:len(parts)-1], ".") } } + return contrib } @@ -314,18 +355,38 @@ func getMetadataFilePath(contrib, componentType string) string { return fmt.Sprintf("%s/%s/metadata.yaml", componentType, contrib) } -func reportResults(missing []string, componentType string) { - if len(missing) > 0 { - fmt.Printf("Missing registrations: %d\n", len(missing)) - for _, m := range missing { - fmt.Printf(" - %s\n", m) +func reportResults(missingRegistrations, missingBuildTags, missingMetadata []string, componentType string) { + totalIssues := len(missingRegistrations) + len(missingBuildTags) + len(missingMetadata) + + if totalIssues > 0 { + fmt.Printf("\nValidation Results for %s components:\n", componentType) + + if len(missingRegistrations) > 0 { + fmt.Printf("Missing registrations: %d\n", len(missingRegistrations)) + for _, m := range missingRegistrations { + fmt.Printf(" - %s\n", m) + } + } + + if len(missingBuildTags) > 0 { + fmt.Printf("Missing build tags: %d\n", len(missingBuildTags)) + for _, m := range missingBuildTags { + fmt.Printf(" - %s\n", m) + } + } + + if len(missingMetadata) > 0 { + fmt.Printf("Missing metadata files: %d\n", len(missingMetadata)) + for _, m := range missingMetadata { + fmt.Printf(" - %s\n", m) + } } } else { - fmt.Printf("All %s components are registered\n", componentType) + fmt.Printf("All %s components are properly configured\n", componentType) } } -func parseContribComponents(output, componentType string) []string { +func parseContribComponents(output, componentType string, ignoreContribComponents []string) []string { components := []string{} lines := strings.FieldsFunc(output, func(r rune) bool { return r == '\n' @@ -339,7 +400,9 @@ func parseContribComponents(output, componentType string) []string { componentName := extractComponentNameFromPath(line, componentType) if componentName != "" { - components = append(components, componentName) + if !slices.Contains(ignoreContribComponents, componentName) { + components = append(components, componentName) + } } } @@ -377,6 +440,41 @@ func extractComponentNameFromPath(filePath, componentType string) string { return "" } +// getRegistrationFileName converts a component name to its registration file name. +// Note: runtime does not deliniate versions in file names so we ignore that in pathing. +// EX: aws.bedrock -> ../dapr/cmd/daprd/components/conversation_bedrock.go +// EX: alicloud.tablestore -> ../dapr/cmd/daprd/components/state_alicloud_tablestore.go +// EX: postgresql.v1 -> ../dapr/cmd/daprd/components/state_postgres.go +func getRegistrationFileName(contrib, componentType string) string { + // For versioned components, strip the version suffix + parts := strings.Split(contrib, ".") + if len(parts) >= 2 { + // Check if the last part is a version (v1, v2, etc.) + lastPart := parts[len(parts)-1] + if strings.HasPrefix(lastPart, "v") && len(lastPart) <= 3 { + // Remove the version part + baseName := strings.Join(parts[:len(parts)-1], ".") + fileName := strings.ReplaceAll(baseName, ".", "_") + // TODO: update runtime file names to match this + if fileName == "postgresql" { + fileName = "postgres" + } + fmt.Printf("sam the file name: %s\n", fileName) + return fmt.Sprintf("../dapr/cmd/daprd/components/%s_%s.go", componentType, fileName) + } + } + + // TODO: update runtime to match??? it's bc contrib has it as hashicorp.consul but runtime has it as "consul" + if contrib == "hashicorp.consul" { + normalizedName := normalizeComponentName(contrib) + fileName := strings.ReplaceAll(normalizedName, ".", "_") + return fmt.Sprintf("../dapr/cmd/daprd/components/%s_%s.go", componentType, fileName) + } + + fileName := strings.ReplaceAll(contrib, ".", "_") + return fmt.Sprintf("../dapr/cmd/daprd/components/%s_%s.go", componentType, fileName) +} + // parseRegisteredComponents parses the output of the grep command to find all registered components func parseRegisteredComponents(output string) []string { components := []string{} diff --git a/conversation/echo/metadata.yaml b/conversation/echo/metadata.yaml new file mode 100644 index 0000000000..734bbb9197 --- /dev/null +++ b/conversation/echo/metadata.yaml @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: conversation +name: echo +version: v1 +status: alpha +title: "Echo" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-conversation/echo/ +metadata: \ No newline at end of file diff --git a/state/aerospike/aerospike.go b/state/aerospike/aerospike.go index 27206519b6..ea95c1516d 100644 --- a/state/aerospike/aerospike.go +++ b/state/aerospike/aerospike.go @@ -34,9 +34,9 @@ import ( ) type aerospikeMetadata struct { - Hosts string - Namespace string - Set string // optional + Hosts string `json:"hosts"` + Namespace string `json:"namespace"` + Set string `json:"set"` // optional } var ( diff --git a/state/aerospike/metadata.yaml b/state/aerospike/metadata.yaml new file mode 100644 index 0000000000..379cbf74f1 --- /dev/null +++ b/state/aerospike/metadata.yaml @@ -0,0 +1,26 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: state +name: aerospike +version: v1 +status: alpha +title: "Aerospike" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-aerospike/ +metadata: + - name: hosts + type: string + required: true + description: Comma-separated list of Aerospike hosts. + example: "localhost:3000,aerospike:3000" + - name: namespace + type: string + required: true + description: The Aerospike namespace. + example: "test" + - name: set + type: string + required: false + description: The Aerospike set name. + example: "dapr-state" \ No newline at end of file diff --git a/state/alicloud/tablestore/metadata.yaml b/state/alicloud/tablestore/metadata.yaml new file mode 100644 index 0000000000..cf5f1215cc --- /dev/null +++ b/state/alicloud/tablestore/metadata.yaml @@ -0,0 +1,36 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: state +name: alicloud.tablestore +version: v1 +status: alpha +title: "AliCloud TableStore" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-state-stores/ +metadata: + - name: endpoint + type: string + required: true + description: The endpoint of the AliCloud TableStore instance. + example: "https://tablestore.aliyuncs.com" + - name: accessKeyID + type: string + required: true + description: The access key ID of the AliCloud TableStore instance. + example: "my_access_key_id" + - name: accessKey + type: string + required: true + description: The access key of the AliCloud TableStore instance. + example: "my_access_key" + - name: instanceName + type: string + required: true + description: The name of the AliCloud TableStore instance. + example: "my_instance" + - name: tableName + type: string + required: true + description: The name of the table to use. + example: "my_table" \ No newline at end of file diff --git a/state/couchbase/couchbase.go b/state/couchbase/couchbase.go index 32264c78ef..562f748286 100644 --- a/state/couchbase/couchbase.go +++ b/state/couchbase/couchbase.go @@ -57,12 +57,12 @@ type Couchbase struct { } type couchbaseMetadata struct { - CouchbaseURL string - Username string - Password string - BucketName string - NumReplicasDurableReplication uint - NumReplicasDurablePersistence uint + CouchbaseURL string `json:"couchbaseURL"` + Username string `json:"username"` + Password string `json:"password"` + BucketName string `json:"bucketName"` + NumReplicasDurableReplication uint `json:"numReplicasDurableReplication"` + NumReplicasDurablePersistence uint `json:"numReplicasDurablePersistence"` } // NewCouchbaseStateStore returns a new couchbase state store. diff --git a/state/couchbase/metadata.yaml b/state/couchbase/metadata.yaml new file mode 100644 index 0000000000..6b30d80448 --- /dev/null +++ b/state/couchbase/metadata.yaml @@ -0,0 +1,43 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: state +name: couchbase +version: v1 +status: alpha +title: "Couchbase" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-couchbase/ +authenticationProfiles: + - title: "Connection String" + description: "Connect to Couchbase using a connection string." + metadata: + - name: couchbaseURL + type: string + required: true + description: The Couchbase connection string. + example: "couchbase://localhost" + - name: username + type: string + required: true + - name: password + type: string + required: true + - name: bucketName + type: string + required: true + description: The Couchbase bucket name. + example: "default" +metadata: + - name: numReplicasDurableReplication + type: integer + required: false + description: The number of replicas for durable replication. + example: 1 + default: 0 + - name: numReplicasDurablePersistence + type: integer + required: false + description: The number of replicas for durable persistence. + example: 1 + default: 0 \ No newline at end of file diff --git a/state/etcd/etcd.go b/state/etcd/etcd.go index 67272071ee..b7a1db3a69 100644 --- a/state/etcd/etcd.go +++ b/state/etcd/etcd.go @@ -123,7 +123,7 @@ func (e *Etcd) ParseClientFromConfig(etcdConfig *etcdConfig) (*clientv3.Client, config := clientv3.Config{ Endpoints: endpoints, - DialTimeout: 5 * time.Second, + DialTimeout: 5 * time.Second, // TODO: make this configurable TLS: tlsConfig, } client, err := clientv3.New(config) diff --git a/state/etcd/metadata.yaml b/state/etcd/metadata.yaml new file mode 100644 index 0000000000..ccca945337 --- /dev/null +++ b/state/etcd/metadata.yaml @@ -0,0 +1,69 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: state +name: etcd +version: v1 +status: alpha +title: "etcd" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-etcd/ +authenticationProfiles: + - title: "Basic Authentication" + description: "Connect to etcd without TLS encryption." + metadata: + - name: endpoints + type: string + required: true + description: Comma-separated list of etcd endpoints. + example: "localhost:2379" + - title: "TLS Authentication" + description: "Connect to etcd using TLS encryption with certificates." + metadata: + - name: endpoints + type: string + required: true + description: Comma-separated list of etcd endpoints. + example: "localhost:2379" + - name: tlsEnable + type: string + required: true + description: Enable TLS authentication. + example: "true" + - name: ca + type: string + required: true + description: CA certificate for TLS verification. + example: | + -----BEGIN CERTIFICATE----- + XXX + -----END CERTIFICATE----- + - name: cert + type: string + required: true + description: Client certificate for TLS authentication. + example: | + -----BEGIN CERTIFICATE----- + XXX + -----END CERTIFICATE----- + - name: key + type: string + required: true + description: Client private key for TLS authentication. + example: | + -----BEGIN PRIVATE KEY----- + XXX + -----END PRIVATE KEY----- +metadata: + - name: keyPrefixPath + type: string + required: false + description: The key prefix path to use. + example: "my_key_prefix_path" + default: "" + - name: maxTxnOps + type: integer + required: false + description: Maximum number of operations allowed in a transaction. + example: 128 + default: 128 \ No newline at end of file diff --git a/state/gcp/firestore/firestore.go b/state/gcp/firestore/firestore.go index 2e4b8eb62e..6157c6646f 100644 --- a/state/gcp/firestore/firestore.go +++ b/state/gcp/firestore/firestore.go @@ -47,6 +47,9 @@ type Firestore struct { } type firestoreMetadata struct { + // TODO: update these to use camel case instead in future + + // TODO: rm type field? It's stable component but this field is NOT used anywhere except in tests. Type string `json:"type"` ProjectID string `json:"project_id" mapstructure:"project_id"` PrivateKeyID string `json:"private_key_id" mapstructure:"private_key_id"` @@ -219,7 +222,7 @@ func (f *Firestore) Close() error { return nil } -func getGCPClient(ctx context.Context, metadata *firestoreMetadata, l logger.Logger) (*datastore.Client, error) { +func getGCPClient(_ context.Context, metadata *firestoreMetadata, l logger.Logger) (*datastore.Client, error) { var gcpClient *datastore.Client var err error diff --git a/state/gcp/firestore/metadata.yaml b/state/gcp/firestore/metadata.yaml new file mode 100644 index 0000000000..0a1f3ef13b --- /dev/null +++ b/state/gcp/firestore/metadata.yaml @@ -0,0 +1,83 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: state +name: gcp.firestore +version: v1 +status: stable +title: "GCP Firestore" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-firestore/ +authenticationProfiles: + - title: "Service Account Authentication" + description: "Authenticate using a GCP service account JSON key." + metadata: + - name: project_id + type: string + required: true + description: The GCP project ID. + example: "my-project" + - name: private_key_id + type: string + required: true + description: The private key ID from the service account JSON. + example: "abc123def456" + - name: private_key + type: string + required: true + description: The private key from the service account JSON. + example: | + -----BEGIN PRIVATE KEY----- + MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC6VJUIbJjbCWsN + # ... (truncated) ... + -----END PRIVATE KEY----- + - name: client_email + type: string + required: true + description: The client email from the service account JSON. + example: "firestore@my-project.iam.gserviceaccount.com" + - name: client_id + type: string + required: true + description: The client ID from the service account JSON. + example: "123456789012345678901" + - name: auth_uri + type: string + required: true + description: The authentication URI from the service account JSON. + example: "https://accounts.google.com/o/oauth2/auth" + - name: token_uri + type: string + required: true + description: The token URI from the service account JSON. + example: "https://oauth2.googleapis.com/token" + - name: auth_provider_x509_cert_url + type: string + required: true + description: The auth provider certificate URL from the service account JSON. + example: "https://www.googleapis.com/oauth2/v1/certs" + - name: client_x509_cert_url + type: string + required: true + description: The client certificate URL from the service account JSON. + example: "https://www.googleapis.com/robot/v1/metadata/x509/firestore%40my-project.iam.gserviceaccount.com" + - title: "Implicit Credentials" + description: "Authenticate using implicit credentials (Application Default Credentials) for local development." + metadata: + - name: project_id + type: string + required: true + description: The GCP project ID. + example: "my-project" + - name: endpoint + type: string + required: false + description: The Firestore endpoint URL (for custom endpoints or emulator). + example: "https://firestore.googleapis.com" +metadata: + - name: entity_kind + type: string + required: false + description: The entity kind (collection name) for storing state data. + example: "dapr-state" + default: "DaprState" \ No newline at end of file diff --git a/state/hashicorp/consul/metadata.yaml b/state/hashicorp/consul/metadata.yaml new file mode 100644 index 0000000000..82c3ce0ce1 --- /dev/null +++ b/state/hashicorp/consul/metadata.yaml @@ -0,0 +1,36 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: state +name: hashicorp.consul +version: v1 +status: alpha +title: "Echo" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-consul/ +metadata: + - name: datacenter + type: string + required: true + description: The datacenter to use. + example: "dc1" + - name: httpAddr + type: string + required: true + description: The HTTP address of the Consul server. + example: "http://localhost:8500" + - name: aclToken + type: string + required: true + description: The ACL token to use. + example: "my_acl_token" + - name: scheme + type: string + required: true + description: The scheme to use. + example: "http" + - name: keyPrefixPath + type: string + required: true + description: The key prefix path to use. + example: "my_key_prefix_path" \ No newline at end of file diff --git a/state/hazelcast/hazelcast.go b/state/hazelcast/hazelcast.go index 2fe56f0488..2144942eab 100644 --- a/state/hazelcast/hazelcast.go +++ b/state/hazelcast/hazelcast.go @@ -40,8 +40,8 @@ type Hazelcast struct { } type hazelcastMetadata struct { - HazelcastServers string - HazelcastMap string + HazelcastServers string `json:"hazelcastServers" mapstructure:"hazelcastServers,servers"` + HazelcastMap string `json:"hazelcastMap" mapstructure:"hazelcastMap,map"` } // NewHazelcastStore returns a new hazelcast backed state store. diff --git a/state/hazelcast/metadata.yaml b/state/hazelcast/metadata.yaml new file mode 100644 index 0000000000..e30ac89d56 --- /dev/null +++ b/state/hazelcast/metadata.yaml @@ -0,0 +1,21 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: state +name: hazelcast +version: v1 +status: alpha +title: "Hazelcast" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-hazelcast/ +metadata: + - name: servers + type: string + required: true + description: Comma-separated list of Hazelcast servers. + example: "localhost:5701" + - name: map + type: string + required: true + description: The Hazelcast map name. + example: "dapr-state" \ No newline at end of file diff --git a/state/jetstream/metadata.yaml b/state/jetstream/metadata.yaml new file mode 100644 index 0000000000..509ea12f51 --- /dev/null +++ b/state/jetstream/metadata.yaml @@ -0,0 +1,37 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: state +name: jetstream +version: v1 +status: alpha +title: "JetStream KV" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-jetstream-kv/ +metadata: + - name: name + type: string + required: false + description: The name of the bucket to use. + example: "my_bucket" + default: "dapr.io - statestore.jetstream" + - name: natsURL + type: string + required: true + description: The NATS URL to use. + example: "nats://localhost:4222" + - name: jwt + type: string + required: false + description: The JWT to use. + example: "my_jwt" + - name: seedKey + type: string + required: false + description: The seed key to use. + example: "my_seed_key" + - name: bucket + type: string + required: true + description: The bucket to use. + example: "my_bucket" \ No newline at end of file diff --git a/state/oci/objectstorage/metadata.yaml b/state/oci/objectstorage/metadata.yaml new file mode 100644 index 0000000000..68c0dd26cf --- /dev/null +++ b/state/oci/objectstorage/metadata.yaml @@ -0,0 +1,79 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: state +name: oci.objectstorage +version: v1 +status: alpha +title: "OCI Object Storage" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-oci-objectstorage/ +authenticationProfiles: + - title: "API Key Authentication" + description: "Authenticate using OCI API key with user OCID, fingerprint, and private key." + metadata: + - name: region + type: string + required: true + description: The OCI region where the bucket is located. + example: "us-ashburn-1" + - name: userOCID + type: string + required: true + description: The OCID of the user. + example: "ocid1.user.oc1..example" + - name: fingerPrint + type: string + required: true + description: The fingerprint of the API key. + example: "xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx" + - name: privateKey + type: string + required: true + description: The private key for API authentication. + example: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" + - name: tenancyOCID + type: string + required: true + description: The OCID of the tenancy. + example: "ocid1.tenancy.oc1..example" + - title: "Instance Principal Authentication" + description: "Authenticate using OCI instance principal (automatically available on OCI compute instances)." + metadata: + - name: instancePrincipalAuthentication + type: bool + required: true + description: Set to true to use instance principal authentication. + example: true + default: false + - title: "Configuration File Authentication" + description: "Authenticate using OCI configuration file." + metadata: + - name: configFileAuthentication + type: bool + required: true + description: Set to true to use configuration file authentication. + example: true + default: falsee + - name: configFilePath + type: string + required: true + description: Path to the OCI configuration file. + example: "~/.oci/config" + - name: configFileProfile + type: string + required: false + description: Profile name in the OCI configuration file. + example: "DEFAULT" + default: "" +metadata: + - name: bucketName + type: string + required: true + description: The name of the OCI Object Storage bucket. + example: "my-bucket" + - name: compartmentOCID + type: string + required: true + description: The OCID of the compartment containing the bucket. + example: "ocid1.compartment.oc1..example" \ No newline at end of file diff --git a/state/oci/objectstorage/objectstorage.go b/state/oci/objectstorage/objectstorage.go index d2d87accb1..17a163f431 100644 --- a/state/oci/objectstorage/objectstorage.go +++ b/state/oci/objectstorage/objectstorage.go @@ -51,10 +51,11 @@ const ( privateKeyKey = "privateKey" userKey = "userOCID" bucketNameKey = "bucketName" - metadataTTLKey = "ttlInSeconds" - daprStateStoreMetaLabel = "dapr-state-store" - expiryTimeMetaLabel = "expiry-time-from-ttl" - isoDateTimeFormat = "2006-01-02T15:04:05" + // TODO: this needs to be used or removed below! + metadataTTLKey = "ttlInSeconds" + daprStateStoreMetaLabel = "dapr-state-store" + expiryTimeMetaLabel = "expiry-time-from-ttl" + isoDateTimeFormat = "2006-01-02T15:04:05" ) type StateStore struct { diff --git a/state/oracledatabase/metadata.yaml b/state/oracledatabase/metadata.yaml new file mode 100644 index 0000000000..ca46065bad --- /dev/null +++ b/state/oracledatabase/metadata.yaml @@ -0,0 +1,31 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: state +name: oracledatabase +version: v1 +status: alpha +title: "Oracle Database" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-oracledatabase/ +authenticationProfiles: + - title: "Connection String" + description: "Connect to Oracle Database using a connection string." + metadata: + - name: connectionString + type: string + required: true +metadata: + - name: tableName + type: string + required: false + description: The table name to store state data. + example: "dapr_state" + default: "state" + - name: oracleWalletLocation + type: string + required: false + description: The location of the Oracle wallet. + example: "/path/to/wallet" + default: "" + \ No newline at end of file diff --git a/state/oracledatabase/oracledatabaseaccess.go b/state/oracledatabase/oracledatabaseaccess.go index fdd5374a59..42a9c77aa6 100644 --- a/state/oracledatabase/oracledatabaseaccess.go +++ b/state/oracledatabase/oracledatabaseaccess.go @@ -50,9 +50,9 @@ type oracleDatabaseAccess struct { } type oracleDatabaseMetadata struct { - ConnectionString string - OracleWalletLocation string - TableName string + ConnectionString string `json:"connectionString"` + OracleWalletLocation string `json:"oracleWalletLocation"` + TableName string `json:"tableName"` } // newOracleDatabaseAccess creates a new instance of oracleDatabaseAccess. diff --git a/state/rethinkdb/metadata.yaml b/state/rethinkdb/metadata.yaml new file mode 100644 index 0000000000..a56d3e9b5c --- /dev/null +++ b/state/rethinkdb/metadata.yaml @@ -0,0 +1,81 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: state +name: rethinkdb +version: v1 +status: beta +title: "RethinkDB" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-rethinkdb/ +authenticationProfiles: + - title: "Basic Authentication" + description: "Authenticate using username and password." + metadata: + - name: address + type: string + required: false + description: The RethinkDB server address. + example: "localhost:28015" + - name: addresses + type: string + required: false + description: Comma-separated list of RethinkDB server addresses. + example: "localhost:28015,localhost:28016" + - name: database + type: string + required: true + description: The RethinkDB database name. + example: "dapr" + default: "" + - name: username + type: string + required: false + description: The username for authentication. If not provided, the admin user is used for v1 handshake protocol. + example: "admin" + - name: password + type: string + required: false + description: The password for authentication. This is only used for v1 handshake protocol. + example: "password" +metadata: + - name: table + type: string + required: false + description: The table name to store state data. + example: "daprstate" + default: "daprstate" + - name: archive + type: bool + required: false + description: Whether to archive changes to a separate table. + example: false + default: false + - name: timeout + type: string + required: false + description: Connection timeout duration. + example: "10s" + - name: useJSONNumber + type: bool + required: false + description: Whether to use json.Number instead of float64. + example: false + default: false + - name: numRetries + type: int + required: false + description: Number of times to retry queries on connection errors. + example: 3 + - name: hostDecayDuration + type: string + required: false + description: Host decay duration for weighted host selection. + example: "5m" + default: "5m" + - name: useOpentracing + type: bool + required: false + description: Whether to enable opentracing for queries. + example: false + default: false \ No newline at end of file diff --git a/state/rethinkdb/rethinkdb.go b/state/rethinkdb/rethinkdb.go index 83f93adf6f..11c3251404 100644 --- a/state/rethinkdb/rethinkdb.go +++ b/state/rethinkdb/rethinkdb.go @@ -32,9 +32,12 @@ import ( ) const ( - stateTableNameDefault = "daprstate" - stateTablePKName = "id" - stateArchiveTableName = "daprstate_archive" + stateTableNameDefault = "daprstate" + // TODO: this needs to be exposed as a metadata option? + stateTablePKName = "id" + // TODO: this needs to be exposed as a metadata option + stateArchiveTableName = "daprstate_archive" + // TODO: this needs to be exposed as a metadata option? stateArchiveTablePKName = "key" ) diff --git a/state/sqlite/metadata.yaml b/state/sqlite/metadata.yaml new file mode 100644 index 0000000000..69289f8ee6 --- /dev/null +++ b/state/sqlite/metadata.yaml @@ -0,0 +1,53 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: state +name: sqlite +version: v1 +status: stable +title: "SQLite" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-sqlite/ +authenticationProfiles: + - title: "Connection String" + description: "Authenticate using a connection string." + metadata: + - name: connectionString + type: string + required: true + description: The SQLite database connection string. +metadata: + - name: timeout + type: string + required: false + description: Timeout for database requests in seconds. + example: "20s" + default: "20s" + - name: busyTimeout + type: string + required: false + description: Busy timeout for database operations in seconds. + example: "2s" + default: "2s" + - name: disableWAL + type: bool + required: false + description: Disable WAL journaling. Should not use WAL if database is stored on a network filesystem. + example: false + default: false + - name: tableName + type: string + required: false + description: The name of the table to store state data. + example: "state" + - name: metadataTableName + type: string + required: false + description: The name of the table to store metadata. + example: "metadata" + - name: cleanupInterval + type: string + required: false + description: Interval for cleanup operations in seconds. Set to 0 to disable. + example: "0s" + default: "0s" \ No newline at end of file diff --git a/state/zookeeper/metadata.yaml b/state/zookeeper/metadata.yaml new file mode 100644 index 0000000000..aea852d71e --- /dev/null +++ b/state/zookeeper/metadata.yaml @@ -0,0 +1,39 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: state +name: zookeeper +version: v1 +status: alpha +title: "ZooKeeper" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-zookeeper/ +metadata: + - name: servers + type: string + required: true + description: Comma-separated list of ZooKeeper servers. + example: "localhost:2181" + - name: sessionTimeout + type: string + required: true + description: Session timeout in seconds. + example: "10s" + - name: maxBufferSize + type: integer + required: false + description: The maximum buffer size in bytes. + example: 1048576 + default: 1048576 # 1MB + - name: maxConnBufferSize + type: integer + required: false + description: The maximum connection buffer size in bytes. + example: 1048576 + default: 1048576 # 1MB + - name: keyPrefixPath + type: string + required: false + description: The key prefix path to use. + example: "my_key_prefix_path" + default: "" \ No newline at end of file diff --git a/state/zookeeper/zk.go b/state/zookeeper/zk.go index 418a44b8d3..52bd54793a 100644 --- a/state/zookeeper/zk.go +++ b/state/zookeeper/zk.go @@ -32,6 +32,7 @@ import ( "github.com/dapr/kit/ptr" ) +// TODO: I think we need more defaults set on these metadata fields. const ( anyVersion = -1 defaultMaxBufferSize = 1024 * 1024 From 629dad187d10960516e8a4db4c6214840ed2a515 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Tue, 5 Aug 2025 17:26:33 -0500 Subject: [PATCH 03/32] feat: add pubsub component checks plus missing metadata files Signed-off-by: Samantha Coyle --- .../cmd/cmd-check-component-registrations.go | 31 ++- pubsub/jetstream/metadata.yaml | 196 ++++++++++++++++++ pubsub/kubemq/metadata.yaml | 47 +++++ pubsub/rocketmq/metadata.go | 3 + pubsub/rocketmq/metadata.yaml | 192 +++++++++++++++++ pubsub/rocketmq/rocketmq.go | 2 + 6 files changed, 469 insertions(+), 2 deletions(-) create mode 100644 pubsub/jetstream/metadata.yaml create mode 100644 pubsub/kubemq/metadata.yaml create mode 100644 pubsub/rocketmq/metadata.yaml diff --git a/.build-tools/cmd/cmd-check-component-registrations.go b/.build-tools/cmd/cmd-check-component-registrations.go index 9b92488b9e..86cf83099f 100644 --- a/.build-tools/cmd/cmd-check-component-registrations.go +++ b/.build-tools/cmd/cmd-check-component-registrations.go @@ -48,8 +48,9 @@ This is a required step before an official Dapr release.`, checkConversationComponents() checkStateComponents() + checkPubSubComponents() - // TODO: secretstore, pubsub, binding, config + // TODO: secretstore, binding, config fmt.Println("\nCheck completed!") }, @@ -74,6 +75,15 @@ func checkStateComponents() { checkComponents("state", ignoreDaprComponents, ignoreContribComponents) } +func checkPubSubComponents() { + fmt.Println("\nChecking pubsub components...") + + // mqtt3 = mqtt, so ignore mqtt3 for now in this cli + ignoreDaprComponents := []string{"mqtt3"} + ignoreContribComponents := []string{"mqtt3"} + checkComponents("pubsub", ignoreDaprComponents, ignoreContribComponents) +} + // Note: because this cli cmd changes to the working directory to the root of the repo so pathing is relative to that. func checkComponents(componentType string, ignoreDaprComponents []string, ignoreContribComponents []string) { if err := checkRegistry(componentType); err != nil { @@ -152,7 +162,24 @@ func checkRegistry(componentType string) error { func findComponentsInBothRepos(componentType string, ignoreContribComponents []string) ([]string, []string, error) { // Find all components in components-contrib, excluding utility files - contribCmd := exec.Command("grep", "-rl", "--include=*.go", "--exclude=errors.go", "--exclude=bulk.go", "--exclude=query.go", "func New", componentType) + // Configure exclude list based on component type + var excludeFiles []string + // keeping this here for now since not all components exclude files... will make func param if needed. + switch componentType { + case "state": + excludeFiles = []string{"--exclude=errors.go", "--exclude=bulk.go", "--exclude=query.go"} + case "pubsub": + excludeFiles = []string{"--exclude=envelope.go", "--exclude=responses.go"} + default: + excludeFiles = []string{} + } + + // Build the grep command with dynamic excludes + grepArgs := []string{"-rl", "--include=*.go"} + grepArgs = append(grepArgs, excludeFiles...) + grepArgs = append(grepArgs, "func New", componentType) + + contribCmd := exec.Command("grep", grepArgs...) contribOutput, err := contribCmd.Output() if err != nil { return nil, nil, fmt.Errorf("could not find components within components-contrib: %v", err) diff --git a/pubsub/jetstream/metadata.yaml b/pubsub/jetstream/metadata.yaml new file mode 100644 index 0000000000..22426afcc2 --- /dev/null +++ b/pubsub/jetstream/metadata.yaml @@ -0,0 +1,196 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: pubsub +name: jetstream +version: v1 +status: beta +title: "JetStream" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-pubsub/setup-jetstream/ +authenticationProfiles: + - title: "Basic Authentication" + description: "Connect to NATS JetStream without authentication." + metadata: + - name: natsURL + type: string + required: true + description: The NATS server URL. + example: "nats://localhost:4222" + - title: "Token Authentication" + description: "Connect to NATS JetStream using token authentication." + metadata: + - name: natsURL + type: string + required: true + description: The NATS server URL. + example: "nats://localhost:4222" + - name: token + type: string + required: true + description: The NATS authentication token. + example: "your-auth-token" + - title: "JWT Authentication" + description: "Connect to NATS JetStream using JWT authentication." + metadata: + - name: natsURL + type: string + required: true + description: The NATS server URL. + example: "nats://localhost:4222" + - name: jwt + type: string + required: true + description: The JWT token for authentication. + example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + - name: seedKey + type: string + required: true + description: The seed key for JWT authentication. + example: "SUAELX7XZIRRCNOQKCXQWFN6M3EBKUTBWK6YQKL4QFLKCUQJSEZL7ZCL7KQ" + - title: "TLS Authentication" + description: "Connect to NATS JetStream using TLS client certificates." + metadata: + - name: natsURL + type: string + required: true + description: The NATS server URL. + example: "nats://localhost:4222" + - name: tls_client_cert + type: string + required: true + description: The TLS client certificate. + example: | + -----BEGIN CERTIFICATE----- + XXX + -----END CERTIFICATE----- + - name: tls_client_key + type: string + required: true + description: The TLS client private key. + example: | + -----BEGIN PRIVATE KEY----- + XXX + -----END PRIVATE KEY----- +metadata: + - name: name + type: string + required: false + description: The name of the JetStream connection. + example: "dapr-jetstream" + default: "dapr.io - pubsub.jetstream" + - name: streamName + type: string + required: false + description: The name of the JetStream stream. + example: "my-stream" + - name: durableName + type: string + required: false + description: The durable name for the consumer. + example: "my-durable" + - name: queueGroupName + type: string + required: false + description: The queue group name for load balancing. + example: "my-queue-group" + - name: startSequence + type: integer + required: false + description: The starting sequence number for message delivery. + example: 1 + - name: startTime + type: integer + required: false + description: The starting time (Unix timestamp) for message delivery. + example: 1640995200 + default: 0 + - name: flowControl + type: bool + required: false + description: Enable flow control for the consumer. + example: false + default: false + - name: ackWait + type: string + required: false + description: The acknowledgment wait time. + example: "30s" + - name: maxDeliver + type: integer + required: false + description: The maximum number of message deliveries. + example: 5 + - name: maxAckPending + type: integer + required: false + description: The maximum number of unacknowledged messages. + example: 100 + - name: replicas + type: integer + required: false + description: The number of stream replicas. + example: 3 + - name: memoryStorage + type: bool + required: false + description: Use memory storage for the stream. + example: false + default: false + - name: rateLimit + type: integer + required: false + description: The rate limit for message consumption. + example: 1000 + - name: heartbeat + type: string + required: false + description: The heartbeat interval. + example: "30s" + - name: deliverPolicy + type: string + required: true + description: The delivery policy (all, last, new, sequence, time). + example: "all" + allowedValues: + - "all" + - "last" + - "new" + - "sequence" + - "time" + - name: ackPolicy + type: string + required: false + description: The acknowledgment policy. + example: "explicit" + allowedValues: + - "explicit" + - "all" + - "none" + default: "explicit" + - name: domain + type: string + required: false + description: The JetStream domain. + example: "my-domain" + - name: apiPrefix + type: string + required: false + description: The API prefix for JetStream. + example: "$JS.API" + - name: concurrency + type: string + required: false + description: The concurrency mode (single, parallel). + example: "single" + default: "single" + - name: backOff + type: array + required: false + description: The backoff configuration for message delivery for the consumer. + example: "[1s, 2s, 4s]" + - name: maxAckPending + type: integer + required: false + description: The maximum number of unacknowledged messages for the consumer. + example: 100 \ No newline at end of file diff --git a/pubsub/kubemq/metadata.yaml b/pubsub/kubemq/metadata.yaml new file mode 100644 index 0000000000..d2d2a6dad8 --- /dev/null +++ b/pubsub/kubemq/metadata.yaml @@ -0,0 +1,47 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: pubsub +name: kubemq +version: v1 +status: beta +title: "KubeMQ" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-pubsub/setup-kubemq/ +authenticationProfiles: + - title: "Token Authentication" + description: "Connect to KubeMQ using an authentication token." + metadata: + - name: authToken + type: string + required: true + description: The authentication token for KubeMQ. + example: "your-auth-token" +metadata: + - name: address + type: string + required: true + description: The KubeMQ server address in format host:port. + example: "localhost:50000" + - name: clientID + type: string + required: false + description: The client ID for the KubeMQ connection. + example: "dapr-client" + - name: group + type: string + required: false + description: The consumer group name. + example: "my-group" + - name: store + type: bool + required: false + description: Whether to use KubeMQ Event Store (true) or Events (false). + example: true + default: true + - name: disableReDelivery + type: bool + required: false + description: Disable message re-delivery on error. + example: false + default: false \ No newline at end of file diff --git a/pubsub/rocketmq/metadata.go b/pubsub/rocketmq/metadata.go index a26d561bba..4817e01b9f 100644 --- a/pubsub/rocketmq/metadata.go +++ b/pubsub/rocketmq/metadata.go @@ -42,6 +42,9 @@ const ( DaprQueueSelector QueueSelectorType = "dapr" ) +// TODO: the time fields in the metadata need to be standardized on either seconds or milliseconds or minutes. +// Right now, it's a mix of all three. + // RocketMQ Go Client Options type rocketMQMetaData struct { // rocketmq instance name, it will be registered to the broker diff --git a/pubsub/rocketmq/metadata.yaml b/pubsub/rocketmq/metadata.yaml new file mode 100644 index 0000000000..f58b49b8e9 --- /dev/null +++ b/pubsub/rocketmq/metadata.yaml @@ -0,0 +1,192 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: pubsub +name: rocketmq +version: v1 +status: alpha +title: "RocketMQ" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-pubsub/setup-rocketmq/ +authenticationProfiles: + - title: "Credential Authentication" + description: "Connect to RocketMQ using access key and secret key." + metadata: + - name: accessKey + type: string + required: true + description: The RocketMQ access key. + example: "your-access-key" + - name: secretKey + type: string + required: true + description: The RocketMQ secret key. + example: "your-secret-key" + - name: securityToken + type: string + required: false + description: The RocketMQ security token. + example: "your-security-token" +metadata: + - name: nameServer + type: string + required: false + description: The RocketMQ name server address. + example: "localhost:9876" + - name: instanceName + type: string + required: false + description: The RocketMQ instance name. + example: "dapr-rocketmq" + - name: producerGroup + type: string + required: false + description: The producer group name. + example: "dapr-producer-group" + - name: consumerGroup + type: string + required: false + description: The consumer group name. + example: "dapr-consumer-group" + - name: nameSpace + type: string + required: false + description: The RocketMQ namespace. + example: "my-namespace" + - name: nameServerDomain + type: string + required: false + description: The RocketMQ name server domain. + example: "rocketmq.example.com" + - name: retries + type: integer + required: false + description: The number of retry attempts for sending messages. + example: 3 + default: 3 + - name: producerQueueSelector + type: string + required: false + description: The producer queue selector type. + example: "hash" + allowedValues: + - "hash" + - "random" + - "manual" + - "roundRobin" + - "dapr" + - name: consumerModel + type: string + required: false + description: The consumer model (clustering, broadcasting). + example: "clustering" + default: "clustering" + - name: fromWhere + type: string + required: false + description: The consumption starting point. + example: "ConsumeFromLastOffset" + allowedValues: + - "ConsumeFromLastOffset" + - "ConsumeFromFirstOffset" + - "ConsumeFromTimestamp" + - name: consumeTimestamp + type: string + required: false + description: The timestamp for consumption starting point. + example: "20220817101902" + - name: consumeOrderly + type: bool + required: false + description: Enable orderly message consumption. + example: false + default: false + - name: consumeMessageBatchMaxSize + type: integer + required: false + description: The maximum batch size for message consumption. + example: 10 + - name: consumeConcurrentlyMaxSpan + type: integer + required: false + description: The maximum span for concurrent consumption. + example: 10 + - name: maxReconsumeTimes + type: integer + required: false + description: The maximum number of reconsume times. -1 means 16 times. + example: 10000 + - name: autoCommit + type: bool + required: false + description: Enable automatic commit. + example: true + default: false + - name: consumeTimeout + type: integer + required: false + description: The consume timeout in minutes. + example: 10 + - name: consumerPullTimeout + type: integer + required: false + description: The consumer pull timeout in milliseconds. + example: 30 + default: 30 + - name: pullInterval + type: integer + required: false + description: The pull interval in minutes. + example: 100 + default: 100 + - name: consumerBatchSize + type: integer + required: false + description: The consumer batch size. + example: 10 + - name: pullBatchSize + type: integer + required: false + description: The pull batch size. + example: 10 + - name: pullThresholdForQueue + type: integer + required: false + description: The pull threshold for queue. + example: 100 + - name: pullThresholdForTopic + type: integer + required: false + description: The pull threshold for topic. + example: 100 + - name: pullThresholdSizeForQueue + type: integer + required: false + description: The pull threshold size for queue. + example: 10 + - name: pullThresholdSizeForTopic + type: integer + required: false + description: The pull threshold size for topic. + example: 10 + - name: content-type + type: string + required: false + description: The content type for messages. + example: "json" + - name: sendTimeOutSec + type: integer + required: false + description: The send timeout in seconds. + example: 10 + - name: logLevel + type: string + required: false + description: The log level. + example: "warn" + default: "warn" + - name: mspProperties + type: string + required: false + description: The message properties to pass to the application (comma-separated). + example: "UNIQ_KEY,MSG_ID" \ No newline at end of file diff --git a/pubsub/rocketmq/rocketmq.go b/pubsub/rocketmq/rocketmq.go index c7fcdece99..d1051a9ba6 100644 --- a/pubsub/rocketmq/rocketmq.go +++ b/pubsub/rocketmq/rocketmq.go @@ -121,6 +121,7 @@ func parseNameServer(nameServer string) []string { } } +// TODO: these metadata fields need to be reevaluated on required vs not. func (r *rocketMQ) setUpConsumer() (mq.PushConsumer, error) { opts := make([]mqc.Option, 0) if r.metadata.InstanceName != "" { @@ -243,6 +244,7 @@ func (r *rocketMQ) setUpConsumer() (mq.PushConsumer, error) { return c, e } +// TODO: these metadata fields need to be reevaluated on required vs not. func (r *rocketMQ) setUpProducer() (mq.Producer, error) { opts := make([]mqp.Option, 0) if r.metadata.InstanceName != "" { From 0c14defe0fe66bf74763805a84e6699721a3c2dd Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Wed, 6 Aug 2025 08:58:28 -0500 Subject: [PATCH 04/32] feat: add secretstores and start on bindings Signed-off-by: Samantha Coyle --- .../cmd/cmd-check-component-registrations.go | 21 +++++---- .../alicloud/parameterstore/metadata.yaml | 45 +++++++++++++++++++ secretstores/huaweicloud/csms/csms.go | 10 ++--- secretstores/huaweicloud/csms/metadata.yaml | 35 +++++++++++++++ secretstores/local/file/filestore.go | 6 +-- secretstores/local/file/metadata.yaml | 29 ++++++++++++ secretstores/tencentcloud/ssm/metadata.yaml | 40 +++++++++++++++++ secretstores/tencentcloud/ssm/ssm.go | 9 ++-- 8 files changed, 175 insertions(+), 20 deletions(-) create mode 100644 secretstores/alicloud/parameterstore/metadata.yaml create mode 100644 secretstores/huaweicloud/csms/metadata.yaml create mode 100644 secretstores/local/file/metadata.yaml create mode 100644 secretstores/tencentcloud/ssm/metadata.yaml diff --git a/.build-tools/cmd/cmd-check-component-registrations.go b/.build-tools/cmd/cmd-check-component-registrations.go index 86cf83099f..f616f50652 100644 --- a/.build-tools/cmd/cmd-check-component-registrations.go +++ b/.build-tools/cmd/cmd-check-component-registrations.go @@ -49,8 +49,10 @@ This is a required step before an official Dapr release.`, checkConversationComponents() checkStateComponents() checkPubSubComponents() + checkSecretStoreComponents() + checkBindingComponents() - // TODO: secretstore, binding, config + // TODO: config fmt.Println("\nCheck completed!") }, @@ -84,6 +86,16 @@ func checkPubSubComponents() { checkComponents("pubsub", ignoreDaprComponents, ignoreContribComponents) } +func checkSecretStoreComponents() { + fmt.Println("\nChecking secretstore components...") + checkComponents("secretstores", []string{}, []string{}) +} + +func checkBindingComponents() { + fmt.Println("\nChecking binding components...") + checkComponents("binding", []string{}, []string{}) +} + // Note: because this cli cmd changes to the working directory to the root of the repo so pathing is relative to that. func checkComponents(componentType string, ignoreDaprComponents []string, ignoreContribComponents []string) { if err := checkRegistry(componentType); err != nil { @@ -491,13 +503,6 @@ func getRegistrationFileName(contrib, componentType string) string { } } - // TODO: update runtime to match??? it's bc contrib has it as hashicorp.consul but runtime has it as "consul" - if contrib == "hashicorp.consul" { - normalizedName := normalizeComponentName(contrib) - fileName := strings.ReplaceAll(normalizedName, ".", "_") - return fmt.Sprintf("../dapr/cmd/daprd/components/%s_%s.go", componentType, fileName) - } - fileName := strings.ReplaceAll(contrib, ".", "_") return fmt.Sprintf("../dapr/cmd/daprd/components/%s_%s.go", componentType, fileName) } diff --git a/secretstores/alicloud/parameterstore/metadata.yaml b/secretstores/alicloud/parameterstore/metadata.yaml new file mode 100644 index 0000000000..72f4fc30ca --- /dev/null +++ b/secretstores/alicloud/parameterstore/metadata.yaml @@ -0,0 +1,45 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: secretstores +name: alicloud.parameterstore +version: v1 +status: alpha +title: "AliCloud OSS Parameter Store" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-secret-stores/alicloud-oss-parameter-store/ +authenticationProfiles: + - title: "Access Key Authentication" + description: "Authenticate using AliCloud access key and secret." + metadata: + - name: regionId + type: string + required: true + description: The AliCloud region ID. + example: "cn-hangzhou" + - name: accessKeyId + type: string + required: true + description: The AliCloud access key ID. + example: "your-access-key-id" + - name: accessKeySecret + type: string + required: true + description: The AliCloud access key secret. + example: "your-access-key-secret" + - name: securityToken + type: string + required: false + description: The AliCloud security token for temporary credentials. + example: "your-security-token" +metadata: + - name: version_id + type: string + required: false + description: The version ID of the parameter to retrieve. If not specified, the latest version is used. + example: "1" + - name: path + type: string + required: false + description: The path prefix for bulk operations. If not specified, root path (/) is used. + example: "/myapp/" \ No newline at end of file diff --git a/secretstores/huaweicloud/csms/csms.go b/secretstores/huaweicloud/csms/csms.go index a0046b5f84..e318e4a5ea 100644 --- a/secretstores/huaweicloud/csms/csms.go +++ b/secretstores/huaweicloud/csms/csms.go @@ -48,9 +48,9 @@ type csmsSecretStore struct { } type CsmsSecretStoreMetadata struct { - Region string - AccessKey string - SecretAccessKey string + Region string `json:"region"` + AccessKey string `json:"accessKey"` + SecretAccessKey string `json:"secretAccessKey"` } // NewHuaweiCsmsSecretStore returns a new Huawei csms secret store. @@ -114,7 +114,7 @@ func (c *csmsSecretStore) BulkGetSecret(ctx context.Context, req secretstores.Bu secret, err := c.GetSecret(ctx, secretstores.GetSecretRequest{ Name: secretName, Metadata: map[string]string{ - versionID: latestVersion, + versionID: latestVersion, // TODO: make this configurable }, }) if err != nil { @@ -130,7 +130,7 @@ func (c *csmsSecretStore) BulkGetSecret(ctx context.Context, req secretstores.Bu // Get all secret names recursively. func (c *csmsSecretStore) getSecretNames(ctx context.Context, marker *string) ([]string, error) { request := &model.ListSecretsRequest{} - limit := pageLimit + limit := pageLimit // TODO: make this configurable request.Limit = &limit request.Marker = marker diff --git a/secretstores/huaweicloud/csms/metadata.yaml b/secretstores/huaweicloud/csms/metadata.yaml new file mode 100644 index 0000000000..8d84931967 --- /dev/null +++ b/secretstores/huaweicloud/csms/metadata.yaml @@ -0,0 +1,35 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: secretstores +name: huaweicloud.csms +version: v1 +status: alpha +title: "HuaweiCloud CSMS" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-secret-stores/huaweicloud-csms/ +authenticationProfiles: + - title: "Access Key Authentication" + description: "Authenticate using HuaweiCloud access key and secret." + metadata: + - name: region + type: string + required: true + description: The HuaweiCloud region. + example: "cn-north-4" + - name: accessKey + type: string + required: true + description: The HuaweiCloud access key. + example: "your-access-key" + - name: secretAccessKey + type: string + required: true + description: The HuaweiCloud secret access key. + example: "your-secret-access-key" +metadata: + - name: version_id + type: string + required: false + description: The version ID of the secret to retrieve. If not specified, the latest version is used. + example: "1" \ No newline at end of file diff --git a/secretstores/local/file/filestore.go b/secretstores/local/file/filestore.go index 27bc71cfbe..a75717a655 100644 --- a/secretstores/local/file/filestore.go +++ b/secretstores/local/file/filestore.go @@ -31,9 +31,9 @@ import ( ) type localSecretStoreMetaData struct { - SecretsFile string - NestedSeparator string - MultiValued bool + SecretsFile string `json:"secretsFile"` + NestedSeparator string `json:"nestedSeparator"` + MultiValued bool `json:"multiValued"` } var _ secretstores.SecretStore = (*localSecretStore)(nil) diff --git a/secretstores/local/file/metadata.yaml b/secretstores/local/file/metadata.yaml new file mode 100644 index 0000000000..a2586be84d --- /dev/null +++ b/secretstores/local/file/metadata.yaml @@ -0,0 +1,29 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: secretstores +name: local.file +version: v1 +status: stable +title: "Local File Secret Store" +description: "Read secrets from a local JSON file for local development." +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-secret-stores/file-secret-store/ +metadata: + - name: secretsFile + type: string + required: true + description: Path to the JSON file containing secrets. + example: "secrets.json" + - name: nestedSeparator + type: string + required: false + description: Separator used for nested keys in the JSON file. + example: ":" + default: ":" + - name: multiValued + type: bool + required: false + description: If true, enables multiple key-values per secret feature. + example: false + default: false \ No newline at end of file diff --git a/secretstores/tencentcloud/ssm/metadata.yaml b/secretstores/tencentcloud/ssm/metadata.yaml new file mode 100644 index 0000000000..1194b7cdc8 --- /dev/null +++ b/secretstores/tencentcloud/ssm/metadata.yaml @@ -0,0 +1,40 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: secretstores +name: tencentcloud.ssm +version: v1 +status: alpha +title: "TencentCloud Secret Manager" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-secret-stores/ +authenticationProfiles: + - title: "Secret Key Authentication" + description: "Authenticate using TencentCloud secret ID and key." + metadata: + - name: secretId + type: string + required: true + description: The TencentCloud secret ID. + example: "your-secret-id" + - name: secretKey + type: string + required: true + description: The TencentCloud secret key. + example: "your-secret-key" + - name: token + type: string + required: false + description: The TencentCloud temporary token for temporary credentials. + example: "your-token" + - name: region + type: string + required: true + description: The TencentCloud region. + example: "ap-guangzhou" +metadata: + - name: VersionID + type: string + required: false + description: The version ID of the secret to retrieve. + example: "1" \ No newline at end of file diff --git a/secretstores/tencentcloud/ssm/ssm.go b/secretstores/tencentcloud/ssm/ssm.go index 192d195f67..42de9bb48b 100644 --- a/secretstores/tencentcloud/ssm/ssm.go +++ b/secretstores/tencentcloud/ssm/ssm.go @@ -30,6 +30,7 @@ import ( ) const ( + // TODO: lowercase these and add to metadata struct eventually VersionID = "VersionID" RequestID = "RequestID" ValueType = "SecretValueType" @@ -56,10 +57,10 @@ type ssmSecretStore struct { } type SsmMetadata struct { - SecretID string - SecretKey string - Token string - Region string + SecretID string `json:"secretId"` + SecretKey string `json:"secretKey"` + Token string `json:"token"` + Region string `json:"region"` } // NewSSM returns a new TencentCloud ssm secret store. From 4ec9db89b19d04cc423df9a393673f0821f704da Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Wed, 6 Aug 2025 17:07:17 -0500 Subject: [PATCH 05/32] feat: add bindings Signed-off-by: Samantha Coyle --- .../cmd/cmd-check-component-registrations.go | 246 +++++++++++++----- .../alicloud/dingtalk/webhook/metadata.yaml | 31 +++ bindings/alicloud/oss/metadata.yaml | 44 ++++ bindings/alicloud/sls/metadata.yaml | 52 ++++ bindings/alicloud/sls/sls.go | 1 + bindings/alicloud/tablestore/metadata.yaml | 43 +++ bindings/alicloud/tablestore/tablestore.go | 4 +- bindings/apns/apns.go | 2 + bindings/apns/metadata.yaml | 41 +++ bindings/aws/dynamodb/dynamodb.go | 1 + bindings/aws/dynamodb/metadata.yaml | 44 ++++ bindings/aws/kinesis/kinesis.go | 3 +- bindings/aws/kinesis/metadata.yaml | 62 +++++ bindings/aws/ses/metadata.yaml | 60 +++++ bindings/aws/ses/ses.go | 1 + bindings/aws/sqs/metadata.yaml | 46 ++++ bindings/aws/sqs/sqs.go | 1 + .../gremlinapi}/cosmosdbgremlinapi.go | 0 .../gremlinapi}/cosmosdbgremlinapi_test.go | 0 .../gremlinapi}/metadata.yaml | 0 .../servicebusqueues/servicebusqueues.go | 2 +- bindings/cloudflare/queues/metadata.yaml | 64 +++++ bindings/commercetools/commercetools.go | 12 +- bindings/commercetools/metadata.yaml | 48 ++++ bindings/dubbo/metadata.yaml | 41 +++ bindings/gcp/pubsub/metadata.yaml | 29 +++ bindings/gcp/pubsub/pubsub.go | 25 +- bindings/gcp/pubsub/pubsub_test.go | 4 +- bindings/graphql/metadata.yaml | 21 ++ bindings/huawei/obs/metadata.yaml | 43 +++ bindings/influx/metadata.yaml | 43 +++ bindings/kitex/metadata.yaml | 33 +++ bindings/kubemq/metadata.yaml | 56 ++++ bindings/kubernetes/kubernetes.go | 6 +- bindings/kubernetes/metadata.yaml | 33 +++ bindings/localstorage/metadata.yaml | 25 ++ bindings/mqtt3/metadata.go | 18 +- bindings/mqtt3/metadata.yaml | 64 +++++ bindings/postmark/metadata.yaml | 45 ++++ bindings/rethinkdb/statechange/metadata.yaml | 87 +++++++ bindings/rocketmq/metadata.yaml | 77 ++++++ bindings/rocketmq/settings.go | 2 +- bindings/sftp/metadata.yaml | 80 ++++++ bindings/smtp/metadata.yaml | 70 +++++ bindings/twilio/sendgrid/metadata.yaml | 55 ++++ bindings/twilio/sms/metadata.yaml | 45 ++++ bindings/twilio/sms/sms.go | 4 - bindings/wasm/metadata.yaml | 26 ++ 48 files changed, 1628 insertions(+), 112 deletions(-) create mode 100644 bindings/alicloud/dingtalk/webhook/metadata.yaml create mode 100644 bindings/alicloud/oss/metadata.yaml create mode 100644 bindings/alicloud/sls/metadata.yaml create mode 100644 bindings/alicloud/tablestore/metadata.yaml create mode 100644 bindings/apns/metadata.yaml create mode 100644 bindings/aws/dynamodb/metadata.yaml create mode 100644 bindings/aws/kinesis/metadata.yaml create mode 100644 bindings/aws/ses/metadata.yaml create mode 100644 bindings/aws/sqs/metadata.yaml rename bindings/azure/{cosmosdbgremlinapi => cosmosdb/gremlinapi}/cosmosdbgremlinapi.go (100%) rename bindings/azure/{cosmosdbgremlinapi => cosmosdb/gremlinapi}/cosmosdbgremlinapi_test.go (100%) rename bindings/azure/{cosmosdbgremlinapi => cosmosdb/gremlinapi}/metadata.yaml (100%) create mode 100644 bindings/cloudflare/queues/metadata.yaml create mode 100644 bindings/commercetools/metadata.yaml create mode 100644 bindings/dubbo/metadata.yaml create mode 100644 bindings/gcp/pubsub/metadata.yaml create mode 100644 bindings/graphql/metadata.yaml create mode 100644 bindings/huawei/obs/metadata.yaml create mode 100644 bindings/influx/metadata.yaml create mode 100644 bindings/kitex/metadata.yaml create mode 100644 bindings/kubemq/metadata.yaml create mode 100644 bindings/kubernetes/metadata.yaml create mode 100644 bindings/localstorage/metadata.yaml create mode 100644 bindings/mqtt3/metadata.yaml create mode 100644 bindings/postmark/metadata.yaml create mode 100644 bindings/rethinkdb/statechange/metadata.yaml create mode 100644 bindings/rocketmq/metadata.yaml create mode 100644 bindings/sftp/metadata.yaml create mode 100644 bindings/smtp/metadata.yaml create mode 100644 bindings/twilio/sendgrid/metadata.yaml create mode 100644 bindings/twilio/sms/metadata.yaml create mode 100644 bindings/wasm/metadata.yaml diff --git a/.build-tools/cmd/cmd-check-component-registrations.go b/.build-tools/cmd/cmd-check-component-registrations.go index f616f50652..ecf37c866f 100644 --- a/.build-tools/cmd/cmd-check-component-registrations.go +++ b/.build-tools/cmd/cmd-check-component-registrations.go @@ -92,8 +92,11 @@ func checkSecretStoreComponents() { } func checkBindingComponents() { - fmt.Println("\nChecking binding components...") - checkComponents("binding", []string{}, []string{}) + fmt.Println("\nChecking bindings components...") + // ignore servicebus.queues as runtime has an alias on this so we're checking for servicebusqueues + ignoreDaprComponents := []string{"mqtt3", "azure.servicebus.queues", "postgresql"} + ignoreContribComponents := []string{} //[]string{"postgresql"} + checkComponents("bindings", ignoreDaprComponents, ignoreContribComponents) } // Note: because this cli cmd changes to the working directory to the root of the repo so pathing is relative to that. @@ -123,17 +126,27 @@ func checkComponents(componentType string, ignoreDaprComponents []string, ignore } daprComponents = filteredDaprComponents + filteredContribComponents := make([]string, 0, len(contribComponents)) + for _, comp := range contribComponents { + if !slices.Contains(ignoreContribComponents, comp) { + filteredContribComponents = append(filteredContribComponents, comp) + } else { + fmt.Printf("Ignoring component: %s\n", comp) + } + } + contribComponents = filteredContribComponents + // Apply vendor prefix mapping and deduplication to both lists. // This removes things like the CSP prefixing. mappedContribComponents := mapAndDeduplicateComponents(contribComponents) mappedDaprComponents := mapAndDeduplicateComponents(daprComponents) fmt.Printf("Components in contrib: %d\n", len(mappedContribComponents)) - fmt.Printf("Components registered: %d\n", len(mappedDaprComponents)) + fmt.Printf("Components registered in runtime: %d\n", len(mappedDaprComponents)) if len(mappedContribComponents) != len(mappedDaprComponents) { - fmt.Printf("\nNumber of components in contrib and dapr/dapr do not match") - fmt.Printf("Contrib: %v\n", mappedContribComponents) + fmt.Println("\nNumber of components in contrib and dapr/dapr do not match") + fmt.Printf("Contrib: %v\n\n", mappedContribComponents) fmt.Printf("Dapr: %v\n", mappedDaprComponents) return } @@ -156,17 +169,44 @@ func checkRegistry(componentType string) error { } // Check for RegisterComponent() - registerComponentCmd := exec.Command("grep", "-r", `Registry) RegisterComponent(componentFactory func(logger.Logger)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) - _, err = registerComponentCmd.Output() - if err != nil { - return fmt.Errorf("could not find RegisterComponent method: %v", err) - } + if componentType != "bindings" { + registerComponentCmd := exec.Command("grep", "-r", `Registry) RegisterComponent(componentFactory func(logger.Logger)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) + _, err = registerComponentCmd.Output() + if err != nil { + return fmt.Errorf("could not find RegisterComponent method: %v", err) + } - // Check for Create() - createCmd := exec.Command("grep", "-r", `Registry) Create(name, version, logName string)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) - _, err = createCmd.Output() - if err != nil { - return fmt.Errorf("could not find Create method: %v", err) + // Check for Create() + createCmd := exec.Command("grep", "-r", `Registry) Create(name, version, logName string)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) + _, err = createCmd.Output() + if err != nil { + return fmt.Errorf("could not find Create method: %v", err) + } + } else { + registerInputBindingCmd := exec.Command("grep", "-r", `Registry) RegisterInputBinding(componentFactory func(logger.Logger)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) + _, err = registerInputBindingCmd.Output() + if err != nil { + return fmt.Errorf("could not find registerInputBindingCmd method: %v", err) + } + + registerOutputBindingCmd := exec.Command("grep", "-r", `Registry) RegisterOutputBinding(componentFactory func(logger.Logger)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) + _, err = registerOutputBindingCmd.Output() + if err != nil { + return fmt.Errorf("could not find registerOutputBindingCmd method: %v", err) + } + + // Check for Creates + createInputBindingCmd := exec.Command("grep", "-r", `Registry) CreateInputBinding(name, version, logName string)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) + _, err = createInputBindingCmd.Output() + if err != nil { + return fmt.Errorf("could not find CreateInputBinding method: %v", err) + } + + createOutputBindingCmd := exec.Command("grep", "-r", `Registry) CreateOutputBinding(name, version, logName string)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) + _, err = createOutputBindingCmd.Output() + if err != nil { + return fmt.Errorf("could not find CreateInputBinding method: %v", err) + } } return nil @@ -182,6 +222,8 @@ func findComponentsInBothRepos(componentType string, ignoreContribComponents []s excludeFiles = []string{"--exclude=errors.go", "--exclude=bulk.go", "--exclude=query.go"} case "pubsub": excludeFiles = []string{"--exclude=envelope.go", "--exclude=responses.go"} + case "bindings": + excludeFiles = []string{"--exclude=client.go"} default: excludeFiles = []string{} } @@ -198,14 +240,24 @@ func findComponentsInBothRepos(componentType string, ignoreContribComponents []s } // Find all registered components in dapr/dapr - registeredCmd := exec.Command("sh", "-c", fmt.Sprintf(`grep -r "RegisterComponent" ../dapr/cmd/daprd/components/%s_*.go`, componentType)) - registeredOutput, err := registeredCmd.Output() - if err != nil { - return nil, nil, fmt.Errorf("could not find all registered components in dapr/dapr: %v", err) + var registeredOutput []byte + if componentType != "bindings" { + registeredCmd := exec.Command("sh", "-c", fmt.Sprintf(`grep -r "RegisterComponent" ../dapr/cmd/daprd/components/%s_*.go`, componentType)) + registeredOutput, err = registeredCmd.Output() + if err != nil { + return nil, nil, fmt.Errorf("could not find all registered components in dapr/dapr: %v", err) + } + } else { + // For bindings, capture both RegisterInputBinding and RegisterOutputBinding + registeredCmd := exec.Command("sh", "-c", fmt.Sprintf(`grep -r "RegisterInputBinding\|RegisterOutputBinding" ../dapr/cmd/daprd/components/%s_*.go`, componentType)) + registeredOutput, err = registeredCmd.Output() + if err != nil { + return nil, nil, fmt.Errorf("could not find all registered components in dapr/dapr: %v", err) + } } contribComponents := parseContribComponents(string(contribOutput), componentType, ignoreContribComponents) - registeredComponents := parseRegisteredComponents(string(registeredOutput)) + registeredComponents := parseRegisteredComponents(string(registeredOutput), componentType) return contribComponents, registeredComponents, nil } @@ -221,12 +273,15 @@ func validateComponents(contribComponents []string, componentType string) ([]str metadataErr := checkMetadataFile(contrib, componentType) if registrationErr != nil { + fmt.Printf("sam the registration err: %v\n", registrationErr) missingRegistrations = append(missingRegistrations, contrib) } if buildTagErr != nil { + fmt.Printf("sam the build tag err: %v\n", buildTagErr) missingBuildTags = append(missingBuildTags, contrib) } if metadataErr != nil { + fmt.Printf("sam the metadata err: %v\n", metadataErr) missingMetadata = append(missingMetadata, contrib) } } @@ -236,10 +291,8 @@ func validateComponents(contribComponents []string, componentType string) ([]str func checkComponentRegistration(contrib, componentType string) error { // For registration files, convert component name to file naming convention - // EX: aws.bedrock -> conversation_bedrock.go // EX: alicloud.tablestore -> state_alicloud_tablestore.go compFileName := getRegistrationFileName(contrib, componentType) - fmt.Printf("sam the comp file name: %s\n", compFileName) fileExistsCmd := exec.Command("ls", compFileName) _, err := fileExistsCmd.Output() @@ -258,53 +311,25 @@ func checkComponentRegistration(contrib, componentType string) error { // checkComponentIsActuallyRegisteredInFile basically checks for if the component name string is within the file // to ensure it is properly registered within runtime. func checkComponentIsActuallyRegisteredInFile(contrib, registrationFile string) error { - registrationCheckCmd := exec.Command("grep", "-r", `"`, registrationFile) - registrationOutput, err := registrationCheckCmd.Output() + // Use grep to find the exact component name in quotes + grepCmd := exec.Command("grep", "-q", fmt.Sprintf(`"%s"`, contrib), registrationFile) + _, err := grepCmd.Output() if err != nil { - return fmt.Errorf("could not read registration file: %v", err) - } - - lines := strings.FieldsFunc(string(registrationOutput), func(r rune) bool { - return r == '\n' - }) - - for _, line := range lines { - if strings.Contains(line, "RegisterComponent") { - quoteIndex := strings.Index(line, "\"") - if quoteIndex > 0 { - rest := line[quoteIndex+1:] - endQuoteIndex := strings.Index(rest, "\"") - if endQuoteIndex > 0 { - registeredName := rest[:endQuoteIndex] - // Check exact match - if registeredName == contrib { - return nil - } - // Check vendor prefix mapping (e.g., hashicorp.consul -> consul) - normalizedName := normalizeComponentName(contrib) - fmt.Printf("sam the normalized name: %s with registered name: %s\n", normalizedName, registeredName) - // registeredName can be something like azure.blobstorage, so need to see if contains too - if registeredName == normalizedName || strings.Contains(registeredName, normalizedName) { - return nil - } - } - } - } + return fmt.Errorf("component '%s' not found in registration file '%s'", contrib, registrationFile) } - return fmt.Errorf("component '%s' not found in registration file '%s'", contrib, registrationFile) + return nil } func checkBuildTag(contrib, componentType string) error { compFileName := getRegistrationFileName(contrib, componentType) - buildTagCmd := exec.Command("grep", "-q", "go:build allcomponents", compFileName) + // Check for "go:build allcomponents" (with or without additional conditions) + buildTagCmd := exec.Command("grep", "-q", "allcomponents", compFileName) _, err := buildTagCmd.Output() if err != nil { - return fmt.Errorf("build tag 'go:build allcomponents' not found in %s", compFileName) + return fmt.Errorf("build tag for 'allcomponents' not found in %s", compFileName) } - - fmt.Printf("Build tag found in %s\n", compFileName) return nil } @@ -312,24 +337,27 @@ func checkBuildTag(contrib, componentType string) error { // EX: aws.bedrock -> bedrock // EX: postgresql.v1 -> postgresql // EX: azure.blobstorage.v1 -> blobstorage -// EX: hashicorp.consul -> consul func normalizeComponentName(contrib string) string { + // fmt.Printf("sam the contrib: %s\n", contrib) if !strings.Contains(contrib, ".") { return contrib } parts := strings.Split(contrib, ".") - vendorPrefixes := []string{"hashicorp", "aws", "azure", "gcp", "alicloud", "oci", "cloudflare"} + vendorPrefixes := []string{"hashicorp", "aws", "azure", "gcp", "alicloud", "oci", "cloudflare", "ibm", "tencentcloud", "huaweicloud", "twilio"} versionSuffixes := []string{"v1", "v2", "internal"} // Handle 2-part names (vendor.component) if len(parts) == 2 { + // fmt.Printf("sam the parts: %v\n", parts) // Strip vendor prefixes if slices.Contains(vendorPrefixes, parts[0]) { + // fmt.Printf("sam the parts[1]: %s\n", parts[1]) return parts[1] } // Strip version suffixes if slices.Contains(versionSuffixes, parts[1]) { + // fmt.Printf("sam the parts[0]: %s\n", parts[0]) return parts[0] } } @@ -353,6 +381,8 @@ func normalizeComponentName(contrib string) string { } } + fmt.Printf("sam the contrib normalized: %s\n", contrib) + return contrib } @@ -498,7 +528,6 @@ func getRegistrationFileName(contrib, componentType string) string { if fileName == "postgresql" { fileName = "postgres" } - fmt.Printf("sam the file name: %s\n", fileName) return fmt.Sprintf("../dapr/cmd/daprd/components/%s_%s.go", componentType, fileName) } } @@ -508,21 +537,71 @@ func getRegistrationFileName(contrib, componentType string) string { } // parseRegisteredComponents parses the output of the grep command to find all registered components -func parseRegisteredComponents(output string) []string { +func parseRegisteredComponents(output string, componentType string) []string { components := []string{} lines := strings.FieldsFunc(output, func(r rune) bool { return r == '\n' }) + // Group lines by file to handle multi-line registrations + fileGroups := make(map[string]bool) for _, line := range lines { - if strings.Contains(line, "RegisterComponent") { - quoteIndex := strings.Index(line, "\"") - if quoteIndex > 0 { - rest := line[quoteIndex+1:] - endQuoteIndex := strings.Index(rest, "\"") - if endQuoteIndex > 0 { - componentName := rest[:endQuoteIndex] - components = append(components, componentName) + // Extract file path from the line (format: filepath:content) + colonIndex := strings.Index(line, ":") + if colonIndex == -1 { + continue + } + filePath := line[:colonIndex] + fileGroups[filePath] = true + } + + // Process each file's content + for filePath := range fileGroups { + + // Read the entire file content + cmd := exec.Command("cat", filePath) + fileContent, err := cmd.Output() + if err != nil { + fmt.Printf("sam the err: %v\n", err) + continue + } + + fileLines := strings.FieldsFunc(string(fileContent), func(r rune) bool { + return r == '\n' + }) + + // Check if this file contains registration calls + componentRegistration := false + lineToContinueFrom := 0 + for i, line := range fileLines { + if componentType == "bindings" { + if strings.Contains(line, "RegisterInputBinding") { + componentRegistration = true + lineToContinueFrom = i + break + } else if strings.Contains(line, "RegisterOutputBinding") { + componentRegistration = true + lineToContinueFrom = i + break + } + } else { + if strings.Contains(line, "RegisterComponent") { + componentRegistration = true + lineToContinueFrom = i + break + } + } + } + + // If file has registration calls, extract all quoted strings from the entire file + if componentRegistration { + for i, line := range fileLines { + if i < lineToContinueFrom { + continue + } + quotedStrings := extractAllQuotedStrings(line) + if len(quotedStrings) > 0 { + components = append(components, quotedStrings...) } } } @@ -530,3 +609,30 @@ func parseRegisteredComponents(output string) []string { return components } + +// extractAllQuotedStrings extracts all quoted strings from a line +func extractAllQuotedStrings(line string) []string { + var quotedStrings []string + start := 0 + for { + quoteIndex := strings.Index(line[start:], "\"") + if quoteIndex == -1 { + break + } + quoteIndex += start + + rest := line[quoteIndex+1:] + endQuoteIndex := strings.Index(rest, "\"") + if endQuoteIndex == -1 { + break + } + + quotedString := rest[:endQuoteIndex] + quotedStrings = append(quotedStrings, quotedString) + + start = quoteIndex + 1 + endQuoteIndex + 1 + } + + fmt.Printf("sam the quotedStrings: %v\n", quotedStrings) + return quotedStrings +} diff --git a/bindings/alicloud/dingtalk/webhook/metadata.yaml b/bindings/alicloud/dingtalk/webhook/metadata.yaml new file mode 100644 index 0000000000..4d88ff3ef8 --- /dev/null +++ b/bindings/alicloud/dingtalk/webhook/metadata.yaml @@ -0,0 +1,31 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: alicloud.dingtalk.webhook +version: v1 +status: alpha +title: "AliCloud DingTalk Webhook" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/alicloud-dingtalk/ +binding: + output: true + input: true + operations: + - name: create + description: "Send a message to DingTalk webhook" + - name: read + description: "Receive messages from DingTalk webhook" +metadata: + - name: id + required: true + description: "The webhook ID" + example: '"your-webhook-id"' + - name: url + required: true + description: "The webhook URL" + example: '"https://oapi.dingtalk.com/robot/send?access_token=your-token"' + - name: secret + required: false + description: "The webhook secret for signature verification" + example: '"your-webhook-secret"' \ No newline at end of file diff --git a/bindings/alicloud/oss/metadata.yaml b/bindings/alicloud/oss/metadata.yaml new file mode 100644 index 0000000000..4831e0a2b2 --- /dev/null +++ b/bindings/alicloud/oss/metadata.yaml @@ -0,0 +1,44 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: alicloud.oss +version: v1 +status: alpha +title: "AliCloud Object Storage Service (OSS)" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/oss/ +binding: + output: true + input: false + operations: + - name: create + description: "Upload file to OSS" +authenticationProfiles: + - title: "AliCloud Access Key Authentication" + description: | + Authenticate using AliCloud access key credentials. + metadata: + - name: accessKeyID + required: false + description: "The AliCloud access key ID" + example: '"your-access-key-id"' + sensitive: true + - name: accessKeySecret + required: false + sensitive: true + description: "The AliCloud access key secret" + example: '"your-access-key-secret"' +metadata: + - name: endpoint + required: true + description: "The OSS endpoint" + example: '"https://oss-cn-hangzhou.aliyuncs.com"' + - name: bucket + required: true + description: "The OSS bucket name" + example: '"your-bucket-name"' + - name: objectKey + required: true + description: "The object key in the bucket" + example: '"path/to/file.txt"' \ No newline at end of file diff --git a/bindings/alicloud/sls/metadata.yaml b/bindings/alicloud/sls/metadata.yaml new file mode 100644 index 0000000000..e7545c84f5 --- /dev/null +++ b/bindings/alicloud/sls/metadata.yaml @@ -0,0 +1,52 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: alicloud.sls +version: v1 +status: alpha +title: "AliCloud Simple Log Storage (SLS)" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/alicloudsls/ +binding: + output: true + input: false + operations: + - name: create + description: "Send logs to SLS" +authenticationProfiles: + - title: "Access Key Authentication" + description: | + Authenticate using AliCloud access key credentials. + metadata: + - name: accessKeyID + required: false + sensitive: true + description: "The AliCloud access key ID" + example: '"your-access-key-id"' + - name: accessKeySecret + required: false + sensitive: true + description: "The AliCloud access key secret" + example: '"your-access-key-secret"' +metadata: + - name: endpoint + required: true + description: "The SLS endpoint" + example: '"https://your-project.cn-hangzhou.log.aliyuncs.com"' + - name: project + required: true + description: "The SLS project name" + example: '"your-project-name"' + - name: logstore + required: true + description: "The SLS logstore name" + example: '"your-logstore-name"' + - name: topic + required: true + description: "The SLS topic name" + example: '"your-topic-name"' + - name: source + required: true + description: "The SLS source name" + example: '"your-source-name"' \ No newline at end of file diff --git a/bindings/alicloud/sls/sls.go b/bindings/alicloud/sls/sls.go index 42d7b4a55c..9cc983736f 100644 --- a/bindings/alicloud/sls/sls.go +++ b/bindings/alicloud/sls/sls.go @@ -60,6 +60,7 @@ func NewAliCloudSlsLogstorage(logger logger.Logger) bindings.OutputBinding { func (s *AliCloudSlsLogstorage) Invoke(ctx context.Context, req *bindings.InvokeRequest) (*bindings.InvokeResponse, error) { // verify the metadata property + // TODO: move these to the struct with proper tags if logProject := req.Metadata["project"]; logProject == "" { return nil, errors.New("SLS binding error: project property not supplied") } diff --git a/bindings/alicloud/tablestore/metadata.yaml b/bindings/alicloud/tablestore/metadata.yaml new file mode 100644 index 0000000000..b4a7ff1335 --- /dev/null +++ b/bindings/alicloud/tablestore/metadata.yaml @@ -0,0 +1,43 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: alicloud.tablestore +version: v1 +status: stable +title: "AliCloud Table Store" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/alicloudtablestore/ +binding: + output: true + input: false + operations: + - name: create + description: "Write data to Table Store" +authenticationProfiles: + - title: "Access Key Authentication" + description: | + Authenticate using AliCloud access key credentials. + metadata: + - name: accessKeyID + required: true + description: "The AliCloud access key ID" + example: '"your-access-key-id"' + - name: accessKeySecret + required: true + sensitive: true + description: "The AliCloud access key secret" + example: '"your-access-key-secret"' +metadata: + - name: endpoint + required: true + description: "The Table Store endpoint" + example: '"https://your-instance.cn-hangzhou.ots.aliyuncs.com"' + - name: instanceName + required: true + description: "The Table Store instance name" + example: '"your-instance-name"' + - name: tableName + required: true + description: "The table name to write to" + example: '"your-table-name"' \ No newline at end of file diff --git a/bindings/alicloud/tablestore/tablestore.go b/bindings/alicloud/tablestore/tablestore.go index c153f2c1e0..e8762e8487 100644 --- a/bindings/alicloud/tablestore/tablestore.go +++ b/bindings/alicloud/tablestore/tablestore.go @@ -137,7 +137,7 @@ func (s *AliCloudTableStore) get(req *bindings.InvokeRequest, resp *bindings.Inv pkNames := strings.Split(req.Metadata[primaryKeys], ",") pks := make([]*tablestore.PrimaryKeyColumn, len(pkNames)) - data := make(map[string]interface{}) + data := make(map[string]any) err := json.Unmarshal(req.Data, &data) if err != nil { return err @@ -313,7 +313,7 @@ func (s *AliCloudTableStore) unmarshal(pks []*tablestore.PrimaryKeyColumn, colum return nil, nil } - data := make(map[string]interface{}) + data := make(map[string]any) for _, pk := range pks { data[pk.ColumnName] = pk.Value diff --git a/bindings/apns/apns.go b/bindings/apns/apns.go index b0a08d4532..abb8a99b68 100644 --- a/bindings/apns/apns.go +++ b/bindings/apns/apns.go @@ -33,6 +33,7 @@ import ( kitmd "github.com/dapr/kit/metadata" ) +// TODO: these should be configured in the metadata.yaml file and be part of the metadata struct with proper json tags. const ( collapseIDKey = "apns-collapse-id" developmentKey = "development" @@ -67,6 +68,7 @@ type APNS struct { authorizationBuilder *authorizationBuilder } +// TODO: use proper tags type APNSmetadata struct { Development bool `mapstructure:"development"` KeyID string `mapstructure:"key-id"` diff --git a/bindings/apns/metadata.yaml b/bindings/apns/metadata.yaml new file mode 100644 index 0000000000..960d1f1971 --- /dev/null +++ b/bindings/apns/metadata.yaml @@ -0,0 +1,41 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: apns +version: v1 +status: alpha +title: "Apple Push Notification Service (APNS)" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/apns/ +binding: + output: true + input: false + operations: + - name: create + description: "Send push notification via APNS" +authenticationProfiles: + - title: "APNS Key Authentication" + description: | + Authenticate using APNS key credentials. + metadata: + - name: key-id + required: true + description: "The APNS key ID" + example: '"ABC123DEF4"' + - name: team-id + required: true + description: "The APNS team ID" + example: '"DEF123GHI4"' + - name: private-key + required: true + sensitive: true + description: "The APNS private key (P8 file content)" + example: '"-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg..."' +metadata: + - name: development + type: boolean + required: false + description: "The APNS environment is development or not" + example: true + default: false \ No newline at end of file diff --git a/bindings/aws/dynamodb/dynamodb.go b/bindings/aws/dynamodb/dynamodb.go index 2096f22433..3f20abdcdd 100644 --- a/bindings/aws/dynamodb/dynamodb.go +++ b/bindings/aws/dynamodb/dynamodb.go @@ -36,6 +36,7 @@ type DynamoDB struct { logger logger.Logger } +// TODO: the metadata fields need updating to use the builtin aws auth provider fully and reflect in metadata.yaml type dynamoDBMetadata struct { Region string `json:"region" mapstructure:"region"` Endpoint string `json:"endpoint" mapstructure:"endpoint"` diff --git a/bindings/aws/dynamodb/metadata.yaml b/bindings/aws/dynamodb/metadata.yaml new file mode 100644 index 0000000000..9581e72b7c --- /dev/null +++ b/bindings/aws/dynamodb/metadata.yaml @@ -0,0 +1,44 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: aws.dynamodb +version: v1 +status: stable +title: "AWS DynamoDB" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/dynamodb/ +binding: + output: true + input: false + operations: + - name: create + description: "Write item to DynamoDB table" +authenticationProfiles: + - title: "AWS Access Key Authentication" + description: | + Authenticate using AWS access key credentials. + metadata: + - name: accessKey + required: true + description: "The AWS access key" + example: '"AKIAIOSFODNN7EXAMPLE"' + - name: secretKey + required: true + sensitive: true + description: "The AWS secret key" + example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"' + - name: sessionToken + required: false + sensitive: true + description: "The AWS session token" + example: '"TOKEN"' + - name: region + required: true + description: "The AWS region" + example: '"us-east-1"' +metadata: + - name: table + required: true + description: "The DynamoDB table name" + example: '"my-table"' \ No newline at end of file diff --git a/bindings/aws/kinesis/kinesis.go b/bindings/aws/kinesis/kinesis.go index bf684f8bbb..47ae3ee4ec 100644 --- a/bindings/aws/kinesis/kinesis.go +++ b/bindings/aws/kinesis/kinesis.go @@ -55,6 +55,7 @@ type AWSKinesis struct { wg sync.WaitGroup } +// TODO: we need to clean up the metadata fields here and update this binding to use the builtin aws auth provider and reflect in metadata.yaml type kinesisMetadata struct { StreamName string `json:"streamName" mapstructure:"streamName"` ConsumerName string `json:"consumerName" mapstructure:"consumerName"` @@ -73,7 +74,7 @@ const ( // SharedThroughput - shared throughput using checkpoint and monitoring. SharedThroughput = "shared" - partitionKeyName = "partitionKey" + partitionKeyName = "partitionKey" // TODO: mv to metadata field instead ) // recordProcessorFactory. diff --git a/bindings/aws/kinesis/metadata.yaml b/bindings/aws/kinesis/metadata.yaml new file mode 100644 index 0000000000..b72e866ee7 --- /dev/null +++ b/bindings/aws/kinesis/metadata.yaml @@ -0,0 +1,62 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: aws.kinesis +version: v1 +status: alpha +title: "AWS Kinesis" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/kinesis/ +binding: + output: true + input: true + operations: + - name: create + description: "Send record to Kinesis stream" + - name: read + description: "Receive records from Kinesis stream" +authenticationProfiles: + - title: "AWS Access Key Authentication" + description: | + Authenticate using AWS access key credentials. + metadata: + - name: accessKey + required: true + description: "The AWS access key" + example: '"AKIAIOSFODNN7EXAMPLE"' + - name: secretKey + required: true + sensitive: true + description: "The AWS secret key" + example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"' + - name: sessionToken + required: false + sensitive: true + description: "The AWS session token" + example: '"TOKEN"' + - name: region + required: true + description: "The AWS region" + example: '"us-east-1"' +metadata: + - name: streamName + required: true + description: "The Kinesis stream name" + example: '"my-stream"' + - name: consumerName + required: false + description: "The consumer name for input binding" + example: '"my-consumer"' + - name: mode + required: false + description: "The consumer mode" + example: '"shared"' + default: '"shared"' + allowedValues: + - "shared" + - "extended" + - name: partitionKey + required: false + description: "The partition key for the Kinesis stream" + example: '"my-partition-key"' \ No newline at end of file diff --git a/bindings/aws/ses/metadata.yaml b/bindings/aws/ses/metadata.yaml new file mode 100644 index 0000000000..d995f112b1 --- /dev/null +++ b/bindings/aws/ses/metadata.yaml @@ -0,0 +1,60 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: aws.ses +version: v1 +status: stable +title: "AWS Simple Email Service (SES)" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/ses/ +binding: + output: true + input: false + operations: + - name: create + description: "Send email via AWS SES" +authenticationProfiles: + - title: "AWS Access Key Authentication" + description: | + Authenticate using AWS access key credentials. + metadata: + - name: accessKey + required: true + description: "The AWS access key" + example: '"AKIAIOSFODNN7EXAMPLE"' + - name: secretKey + required: true + sensitive: true + description: "The AWS secret key" + example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"' + - name: sessionToken + required: false + sensitive: true + description: "The AWS session token" + example: '"TOKEN"' + - name: region + required: true + description: "The AWS region" + example: '"us-east-1"' +metadata: + - name: emailFrom + required: true + description: "The sender email address" + example: '"sender@example.com"' + - name: emailTo + required: true + description: "The recipient email address" + example: '"recipient@example.com"' + - name: subject + required: true + description: "The email subject" + example: '"Hello from Dapr"' + - name: emailCc + required: false + description: "The email CC address" + example: '"cc@example.com"' + - name: emailBcc + required: false + description: "The email BCC address" + example: '"bcc@example.com"' \ No newline at end of file diff --git a/bindings/aws/ses/ses.go b/bindings/aws/ses/ses.go index b8d2ff3faa..2fb9300f6e 100644 --- a/bindings/aws/ses/ses.go +++ b/bindings/aws/ses/ses.go @@ -43,6 +43,7 @@ type AWSSES struct { logger logger.Logger } +// TODO: the metadata fields need updating to use the builtin aws auth provider fully and reflect in metadata.yaml type sesMetadata struct { Region string `json:"region"` AccessKey string `json:"accessKey"` diff --git a/bindings/aws/sqs/metadata.yaml b/bindings/aws/sqs/metadata.yaml new file mode 100644 index 0000000000..47ca0f43d4 --- /dev/null +++ b/bindings/aws/sqs/metadata.yaml @@ -0,0 +1,46 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: aws.sqs +version: v1 +status: alpha +title: "AWS SQS" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/sqs/ +binding: + output: true + input: true + operations: + - name: create + description: "Send message to SQS queue" + - name: read + description: "Receive messages from SQS queue" +authenticationProfiles: + - title: "AWS Access Key Authentication" + description: | + Authenticate using AWS access key credentials. + metadata: + - name: accessKey + required: true + description: "The AWS access key" + example: '"AKIAIOSFODNN7EXAMPLE"' + - name: secretKey + required: true + sensitive: true + description: "The AWS secret key" + example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"' + - name: sessionToken + required: false + sensitive: true + description: "The AWS session token" + example: '"TOKEN"' + - name: region + required: true + description: "The AWS region" + example: '"us-east-1"' +metadata: + - name: queueName + required: true + description: "The SQS queue name" + example: '"my-queue"' \ No newline at end of file diff --git a/bindings/aws/sqs/sqs.go b/bindings/aws/sqs/sqs.go index b09fde61f6..7d979f009e 100644 --- a/bindings/aws/sqs/sqs.go +++ b/bindings/aws/sqs/sqs.go @@ -41,6 +41,7 @@ type AWSSQS struct { closed atomic.Bool } +// TODO: the metadata fields need updating to use the builtin aws auth provider fully and reflect in metadata.yaml type sqsMetadata struct { QueueName string `json:"queueName"` Region string `json:"region"` diff --git a/bindings/azure/cosmosdbgremlinapi/cosmosdbgremlinapi.go b/bindings/azure/cosmosdb/gremlinapi/cosmosdbgremlinapi.go similarity index 100% rename from bindings/azure/cosmosdbgremlinapi/cosmosdbgremlinapi.go rename to bindings/azure/cosmosdb/gremlinapi/cosmosdbgremlinapi.go diff --git a/bindings/azure/cosmosdbgremlinapi/cosmosdbgremlinapi_test.go b/bindings/azure/cosmosdb/gremlinapi/cosmosdbgremlinapi_test.go similarity index 100% rename from bindings/azure/cosmosdbgremlinapi/cosmosdbgremlinapi_test.go rename to bindings/azure/cosmosdb/gremlinapi/cosmosdbgremlinapi_test.go diff --git a/bindings/azure/cosmosdbgremlinapi/metadata.yaml b/bindings/azure/cosmosdb/gremlinapi/metadata.yaml similarity index 100% rename from bindings/azure/cosmosdbgremlinapi/metadata.yaml rename to bindings/azure/cosmosdb/gremlinapi/metadata.yaml diff --git a/bindings/azure/servicebusqueues/servicebusqueues.go b/bindings/azure/servicebusqueues/servicebusqueues.go index bc1291ac32..af2bc3b534 100644 --- a/bindings/azure/servicebusqueues/servicebusqueues.go +++ b/bindings/azure/servicebusqueues/servicebusqueues.go @@ -62,7 +62,7 @@ func (a *AzureServiceBusQueues) Init(ctx context.Context, metadata bindings.Meta return err } - a.client, err = impl.NewClient(a.metadata, metadata.Properties, a.logger) + a.client, err = impl.NewClient(a.metadata, metadata.Properties) if err != nil { return err } diff --git a/bindings/cloudflare/queues/metadata.yaml b/bindings/cloudflare/queues/metadata.yaml new file mode 100644 index 0000000000..ea9eb0077b --- /dev/null +++ b/bindings/cloudflare/queues/metadata.yaml @@ -0,0 +1,64 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: cloudflare.queues +version: v1 +status: alpha +title: "Cloudflare Queues" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/cloudflare-queues/ +binding: + output: true + input: false + operations: + - name: create + description: "Send message to Cloudflare Queue" + - name: read + description: "Receive messages from Cloudflare Queue" +authenticationProfiles: + - title: "API Token Authentication" + description: | + Authenticate using Cloudflare API token and account ID. Dapr will create/manage the worker. + metadata: + - name: cfAPIToken + required: true + sensitive: true + description: "The Cloudflare API token" + example: '"your-api-token"' + - name: cfAccountID + required: true + description: "The Cloudflare account ID" + example: '"your-account-id"' + - name: key + required: true + sensitive: true + description: "The Ed25519 private key in PKCS#8 PEM format for JWT signing" + example: '"-----BEGIN PRIVATE KEY-----\nXXX..."' + - name: workerName + required: true + description: "The worker name for JWT token audience" + example: '"my-worker"' + - title: "Connect to Pre-deployed Worker" + description: | + Connect to a worker that has been pre-deployed and is ready to use. No API tokens needed. + metadata: + - name: workerUrl + required: true + description: "The Cloudflare worker URL" + example: '"https://your-worker.your-subdomain.workers.dev"' + - name: key + required: true + sensitive: true + description: "The Ed25519 private key in PKCS#8 PEM format for JWT signing" + example: '"-----BEGIN PRIVATE KEY-----\nXXX..."' + - name: workerName + required: true + description: "The worker name for JWT token audience" + example: '"my-worker"' +metadata: + - name: timeoutInSeconds + required: false + description: "Timeout for network requests in seconds" + example: '20' + default: '20' \ No newline at end of file diff --git a/bindings/commercetools/commercetools.go b/bindings/commercetools/commercetools.go index d48acba52b..fe36540f5a 100644 --- a/bindings/commercetools/commercetools.go +++ b/bindings/commercetools/commercetools.go @@ -40,12 +40,12 @@ type Data struct { } type commercetoolsMetadata struct { - Region string - Provider string - ProjectKey string - ClientID string - ClientSecret string - Scopes string + Region string `json:"region"` + Provider string `json:"provider"` + ProjectKey string `json:"projectKey"` + ClientID string `json:"clientID"` + ClientSecret string `json:"clientSecret"` + Scopes string `json:"scopes"` } func NewCommercetools(logger logger.Logger) bindings.OutputBinding { diff --git a/bindings/commercetools/metadata.yaml b/bindings/commercetools/metadata.yaml new file mode 100644 index 0000000000..e92118bfba --- /dev/null +++ b/bindings/commercetools/metadata.yaml @@ -0,0 +1,48 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: commercetools +version: v1 +status: alpha +title: "Commercetools" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/commercetools/ +binding: + output: true + input: false + operations: + - name: create + description: "Create resource in Commercetools" +authenticationProfiles: + - title: "OAuth Client Authentication" + description: | + Authenticate using OAuth client credentials. + metadata: + - name: clientID + required: true + description: "The Commercetools client ID" + example: '"your-client-id"' + - name: clientSecret + required: true + sensitive: true + description: "The Commercetools client secret" + example: '"your-client-secret"' +metadata: + - name: projectKey + required: true + description: "The Commercetools project key" + example: '"my-project"' + - name: region + required: true + description: "The Commercetools region" + example: '"gcp-europe-west1"' + default: '"gcp-europe-west1"' - name: provider + required: true + description: "The Commercetools provider" + example: '"gcp"' + default: '"gcp"' + - name: scopes + required: true + description: "The OAuth scopes" + example: '"manage_project:my-project"' diff --git a/bindings/dubbo/metadata.yaml b/bindings/dubbo/metadata.yaml new file mode 100644 index 0000000000..903b7b3446 --- /dev/null +++ b/bindings/dubbo/metadata.yaml @@ -0,0 +1,41 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: dubbo +version: v1 +status: alpha +title: "Apache Dubbo" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/ +binding: + output: true + input: false + operations: + - name: create + description: "Invoke Dubbo service" +metadata: + - name: interfaceName + required: true + description: "The Dubbo interface name" + example: '"com.example.UserService"' + - name: methodName + required: true + description: "The method name to invoke" + example: '"getUser"' + - name: version + required: false + description: "The service version" + example: '"1.0.0"' + - name: group + required: false + description: "The service group" + example: '"mygroup"' + - name: providerHostname + required: false + description: "The provider hostname" + example: '"localhost"' + - name: providerPort + required: false + description: "The provider port" + example: '8080' \ No newline at end of file diff --git a/bindings/gcp/pubsub/metadata.yaml b/bindings/gcp/pubsub/metadata.yaml new file mode 100644 index 0000000000..9927ebe2a1 --- /dev/null +++ b/bindings/gcp/pubsub/metadata.yaml @@ -0,0 +1,29 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: gcp.pubsub +version: v1 +status: alpha +title: "Google Cloud Pub/Sub" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/pubsub/ +binding: + output: true + input: true + operations: + - name: create + description: "Publish message to Pub/Sub topic" + - name: read + description: "Receive messages from Pub/Sub subscription" +builtinAuthenticationProfiles: + - name: "gcp" +metadata: + - name: topic + required: true + description: "The Pub/Sub topic name" + example: '"my-topic"' + - name: subscription + required: false + description: "The Pub/Sub subscription name" + example: '"my-subscription"' \ No newline at end of file diff --git a/bindings/gcp/pubsub/pubsub.go b/bindings/gcp/pubsub/pubsub.go index dfc8a69787..683956c89e 100644 --- a/bindings/gcp/pubsub/pubsub.go +++ b/bindings/gcp/pubsub/pubsub.go @@ -46,19 +46,20 @@ type GCPPubSub struct { wg sync.WaitGroup } +// These JSON tags directly match the builtin auth provider metadata fields for GCP. type pubSubMetadata struct { - Topic string `json:"topic"` - Subscription string `json:"subscription"` - Type string `json:"type"` - ProjectID string `json:"project_id"` - PrivateKeyID string `json:"private_key_id"` - PrivateKey string `json:"private_key"` - ClientEmail string `json:"client_email"` - ClientID string `json:"client_id"` - AuthURI string `json:"auth_uri"` - TokenURI string `json:"token_uri"` - AuthProviderCertURL string `json:"auth_provider_x509_cert_url"` - ClientCertURL string `json:"client_x509_cert_url"` + Topic string `json:"topic"` + Subscription string `json:"subscription"` + Type string `json:"type"` + ProjectID string `json:"projectID"` + PrivateKeyID string `json:"privateKeyID"` + PrivateKey string `json:"privateKey"` + ClientEmail string `json:"clientEmail"` + ClientID string `json:"clientID"` + AuthURI string `json:"authURI"` + TokenURI string `json:"tokenURI"` + AuthProviderX509CertURL string `json:"authProviderX509CertURL"` + ClientX509CertURL string `json:"clientX509CertURL"` } // NewGCPPubSub returns a new GCPPubSub instance. diff --git a/bindings/gcp/pubsub/pubsub_test.go b/bindings/gcp/pubsub/pubsub_test.go index cd4d3f8171..5d0afb436a 100644 --- a/bindings/gcp/pubsub/pubsub_test.go +++ b/bindings/gcp/pubsub/pubsub_test.go @@ -40,9 +40,9 @@ func TestInit(t *testing.T) { assert.Equal(t, "s1", pubsubMeta.Subscription) assert.Equal(t, "t1", pubsubMeta.Topic) - assert.Equal(t, "https://auth", pubsubMeta.AuthProviderCertURL) + assert.Equal(t, "https://auth", pubsubMeta.AuthProviderX509CertURL) assert.Equal(t, "https://auth", pubsubMeta.AuthURI) - assert.Equal(t, "https://cert", pubsubMeta.ClientCertURL) + assert.Equal(t, "https://cert", pubsubMeta.ClientX509CertURL) assert.Equal(t, "test@test.com", pubsubMeta.ClientEmail) assert.Equal(t, "id", pubsubMeta.ClientID) assert.Equal(t, "****", pubsubMeta.PrivateKey) diff --git a/bindings/graphql/metadata.yaml b/bindings/graphql/metadata.yaml new file mode 100644 index 0000000000..1346675225 --- /dev/null +++ b/bindings/graphql/metadata.yaml @@ -0,0 +1,21 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: graphql +version: v1 +status: alpha +title: "GraphQL" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/graphql/ +binding: + output: true + input: false + operations: + - name: create + description: "Execute GraphQL query or mutation" +metadata: + - name: endpoint + required: true + description: "The GraphQL endpoint URL" + example: '"https://api.example.com/graphql"' \ No newline at end of file diff --git a/bindings/huawei/obs/metadata.yaml b/bindings/huawei/obs/metadata.yaml new file mode 100644 index 0000000000..61b980eb89 --- /dev/null +++ b/bindings/huawei/obs/metadata.yaml @@ -0,0 +1,43 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: huawei.obs +version: v1 +status: alpha +title: "Huawei Object Storage Service (OBS)" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/huawei-obs +binding: + output: true + input: false + operations: + - name: create + description: "Upload file to OBS" +authenticationProfiles: + - title: "Access Key Authentication" + description: | + Authenticate using Huawei Cloud access key credentials. + metadata: + - name: accessKey + required: true + description: "The Huawei Cloud access key ID" + example: '"your-access-key-id"' + - name: secretKey + required: true + sensitive: true + description: "The Huawei Cloud secret access key" + example: '"your-secret-access-key"' + - name: region + required: true + description: "The Huawei Cloud region" + example: '"cn-north-4"' +metadata: + - name: endpoint + required: true + description: "The OBS endpoint" + example: '"https://obs.cn-north-4.myhuaweicloud.com"' + - name: bucket + required: true + description: "The OBS bucket name" + example: '"your-bucket-name"' \ No newline at end of file diff --git a/bindings/influx/metadata.yaml b/bindings/influx/metadata.yaml new file mode 100644 index 0000000000..ac78147cc7 --- /dev/null +++ b/bindings/influx/metadata.yaml @@ -0,0 +1,43 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: influx +version: v1 +status: alpha +title: "InfluxDB" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/influxdb/ +binding: + output: true + input: false + operations: + - name: create + description: "Write data points to InfluxDB" +authenticationProfiles: + - title: "Token Authentication" + description: | + Authenticate using InfluxDB token. + metadata: + - name: token + required: true + sensitive: true + description: "The InfluxDB authentication token" + example: '"your-influxdb-token"' +metadata: + - name: url + required: true + description: "The InfluxDB server URL" + example: '"http://localhost:8086"' + - name: org + required: true + description: "The InfluxDB organization name" + example: '"your-org"' + - name: bucket + required: true + description: "The InfluxDB bucket name" + example: '"your-bucket"' + - name: measurement + required: true + description: "The measurement name" + example: '"cpu_usage"' \ No newline at end of file diff --git a/bindings/kitex/metadata.yaml b/bindings/kitex/metadata.yaml new file mode 100644 index 0000000000..6585165e15 --- /dev/null +++ b/bindings/kitex/metadata.yaml @@ -0,0 +1,33 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: kitex +version: v1 +status: alpha +title: "Kitex" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/kitex/ +binding: + output: true + input: false + operations: + - name: create + description: "Invoke Kitex service" +metadata: + - name: serviceName + required: true + description: "The Kitex service name" + example: '"my-service"' + - name: methodName + required: true + description: "The method name to invoke" + example: '"getUser"' + - name: destService + required: true + description: "The destination service name" + example: '"my-service"' + - name: hostPorts + required: true + description: "The service address" + example: '"localhost:8080"' \ No newline at end of file diff --git a/bindings/kubemq/metadata.yaml b/bindings/kubemq/metadata.yaml new file mode 100644 index 0000000000..994d7d4675 --- /dev/null +++ b/bindings/kubemq/metadata.yaml @@ -0,0 +1,56 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: kubemq +version: v1 +status: beta +title: "KubeMQ" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/kubemq/ +binding: + output: true + input: true + operations: + - name: create + description: "Send message to KubeMQ" + - name: read + description: "Receive messages from KubeMQ" +authenticationProfiles: + - title: "Token Authentication" + description: "Connect to KubeMQ using an authentication token." + metadata: + - name: authToken + type: string + required: true + description: The authentication token for KubeMQ. + example: "your-auth-token" +metadata: + - name: address + type: string + required: true + description: The KubeMQ server address in format host:port. + example: "localhost:50000" + - name: channel + type: string + required: true + description: The KubeMQ channel name. + example: "my-channel" + - name: pollMaxItems + type: int + required: false + description: The maximum number of items to poll. + example: 10 + default: 1 + - name: pollTimeoutSeconds + type: int + required: false + description: The timeout in seconds for polling. + example: 3600 + default: 3600 + - name: autoAcknowledged + type: bool + required: false + description: Whether to automatically acknowledge messages. + example: true + default: false \ No newline at end of file diff --git a/bindings/kubernetes/kubernetes.go b/bindings/kubernetes/kubernetes.go index f41bc88cba..0d3bd8a208 100644 --- a/bindings/kubernetes/kubernetes.go +++ b/bindings/kubernetes/kubernetes.go @@ -121,7 +121,7 @@ func (k *kubernetesInput) Read(ctx context.Context, handler bindings.Handler) er &corev1.Event{}, k.metadata.ResyncPeriod, cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { + AddFunc: func(obj any) { if obj != nil { resultChan <- EventResponse{ Event: "add", @@ -132,7 +132,7 @@ func (k *kubernetesInput) Read(ctx context.Context, handler bindings.Handler) er k.logger.Warnf("Nil Object in Add handle %v", obj) } }, - DeleteFunc: func(obj interface{}) { + DeleteFunc: func(obj any) { if obj != nil { resultChan <- EventResponse{ Event: "delete", @@ -143,7 +143,7 @@ func (k *kubernetesInput) Read(ctx context.Context, handler bindings.Handler) er k.logger.Warnf("Nil Object in Delete handle %v", obj) } }, - UpdateFunc: func(oldObj, newObj interface{}) { + UpdateFunc: func(oldObj, newObj any) { if oldObj != nil && newObj != nil { resultChan <- EventResponse{ Event: "update", diff --git a/bindings/kubernetes/metadata.yaml b/bindings/kubernetes/metadata.yaml new file mode 100644 index 0000000000..06693841b7 --- /dev/null +++ b/bindings/kubernetes/metadata.yaml @@ -0,0 +1,33 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: kubernetes +version: v1 +status: alpha +title: "Kubernetes Events" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/kubernetes-binding/ +binding: + output: false + input: true + operations: + - name: read + description: "Read Kubernetes events" +metadata: + - name: namespace + required: false + description: "The Kubernetes namespace" + example: '"default"' + default: '"default"' + - name: kubeconfigPath + required: false + description: "The path to the kubeconfig file" + example: '"~/.kube/config"' + default: '"~/.kube/config"' + - name: resyncPeriodInSec + required: false + type: duration + description: "The resync period in seconds" + example: '30s' + default: '10s' \ No newline at end of file diff --git a/bindings/localstorage/metadata.yaml b/bindings/localstorage/metadata.yaml new file mode 100644 index 0000000000..52ec52b02d --- /dev/null +++ b/bindings/localstorage/metadata.yaml @@ -0,0 +1,25 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: localstorage +version: v1 +status: stable +title: "Local Storage" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/localstorage/ +binding: + output: true + input: false + operations: + - name: create + description: "Write file to local storage" +metadata: + - name: rootPath + required: true + description: "The root directory path" + example: '"/tmp/dapr"' + - name: fileName + required: true + description: "The file name to write" + example: '"data.txt"' \ No newline at end of file diff --git a/bindings/mqtt3/metadata.go b/bindings/mqtt3/metadata.go index b9fd05abe2..5bc38066f1 100644 --- a/bindings/mqtt3/metadata.go +++ b/bindings/mqtt3/metadata.go @@ -25,16 +25,14 @@ import ( const ( // Keys. - mqttURL = "url" - mqttTopic = "topic" - mqttQOS = "qos" // This key is deprecated - mqttRetain = "retain" - mqttClientID = "consumerID" - mqttCleanSession = "cleanSession" - mqttCACert = "caCert" - mqttClientCert = "clientCert" - mqttClientKey = "clientKey" - mqttBackOffMaxRetries = "backOffMaxRetries" + mqttURL = "url" + mqttTopic = "topic" + mqttQOS = "qos" // This key is deprecated + mqttRetain = "retain" + mqttCleanSession = "cleanSession" + mqttCACert = "caCert" + mqttClientCert = "clientCert" + mqttClientKey = "clientKey" // Defaults. defaultQOS = 1 diff --git a/bindings/mqtt3/metadata.yaml b/bindings/mqtt3/metadata.yaml new file mode 100644 index 0000000000..9c14c3b13d --- /dev/null +++ b/bindings/mqtt3/metadata.yaml @@ -0,0 +1,64 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: mqtt3 +version: v1 +status: beta +title: "MQTT v3" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/mqtt3/ +binding: + output: true + input: true + operations: + - name: create + description: "Publish message to MQTT topic" + - name: read + description: "Subscribe to MQTT topic" +authenticationProfiles: + - title: "TLS Authentication" + description: | + Authenticate using TLS certificates. + metadata: + - name: caCert + required: true + description: "CA certificate for TLS" + example: '"-----BEGIN CERTIFICATE-----\n..."' + - name: clientCert + required: true + description: "Client certificate for TLS" + example: '"-----BEGIN CERTIFICATE-----\n..."' + - name: clientKey + required: true + sensitive: true + description: "Client private key for TLS" + example: '"-----BEGIN PRIVATE KEY-----\n..."' +metadata: + - name: url + required: true + description: "The MQTT broker URL" + example: '"tcp://localhost:1883"' + - name: topic + required: true + description: "The MQTT topic" + example: '"my-topic"' + - name: consumerID + required: false + description: "The MQTT client ID" + example: '"my-client"' + - name: retain + required: false + description: "Whether to retain messages" + example: 'false' + default: 'false' + - name: cleanSession + required: false + description: "Whether to use clean session" + example: 'true' + default: 'true' + - name: backOffMaxRetries + required: false + description: "Maximum retries for backoff" + example: '3' + default: '3' \ No newline at end of file diff --git a/bindings/postmark/metadata.yaml b/bindings/postmark/metadata.yaml new file mode 100644 index 0000000000..9670d8b783 --- /dev/null +++ b/bindings/postmark/metadata.yaml @@ -0,0 +1,45 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: postmark +version: v1 +status: alpha +title: "Postmark" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/postmark/ +binding: + output: true + input: false + operations: + - name: create + description: "Send an email using Postmark" +metadata: + - name: serverToken + required: true + description: "The Postmark server token" + example: '"your-server-token"' + - name: accountToken + required: true + description: "The Postmark account token" + example: '"your-account-token"' + - name: emailFrom + required: false + description: "The sender email address" + example: '"sender@example.com"' + - name: emailTo + required: false + description: "The recipient email address" + example: '"recipient@example.com"' + - name: subject + required: false + description: "The email subject" + example: '"Hello from Dapr"' + - name: emailCc + required: false + description: "The CC email address" + example: '"cc@example.com"' + - name: emailBcc + required: false + description: "The BCC email address" + example: '"bcc@example.com"' \ No newline at end of file diff --git a/bindings/rethinkdb/statechange/metadata.yaml b/bindings/rethinkdb/statechange/metadata.yaml new file mode 100644 index 0000000000..4e63d8f1cb --- /dev/null +++ b/bindings/rethinkdb/statechange/metadata.yaml @@ -0,0 +1,87 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: rethinkdb.statechange +version: v1 +status: beta +title: "RethinkDB State Change" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/rethinkdb/ +binding: + output: false + input: true + operations: + - name: read + description: "Listen for state changes in RethinkDB" +authenticationProfiles: + - title: "Basic Authentication" + description: "Authenticate using username and password." + metadata: + - name: address + type: string + required: false + description: The RethinkDB server address. + example: "localhost:28015" + - name: addresses + type: string + required: false + description: Comma-separated list of RethinkDB server addresses. + example: "localhost:28015,localhost:28016" + - name: database + type: string + required: true + description: The RethinkDB database name. + example: "dapr" + default: "" + - name: username + type: string + required: false + description: The username for authentication. If not provided, the admin user is used for v1 handshake protocol. + example: "admin" + - name: password + type: string + required: false + description: The password for authentication. This is only used for v1 handshake protocol. + example: "password" +metadata: + - name: table + type: string + required: false + description: The table name to store state data. + example: "daprstate" + default: "daprstate" + - name: archive + type: bool + required: false + description: Whether to archive changes to a separate table. + example: false + default: false + - name: timeout + type: string + required: false + description: Connection timeout duration. + example: "10s" + - name: useJSONNumber + type: bool + required: false + description: Whether to use json.Number instead of float64. + example: false + default: false + - name: numRetries + type: int + required: false + description: Number of times to retry queries on connection errors. + example: 3 + - name: hostDecayDuration + type: string + required: false + description: Host decay duration for weighted host selection. + example: "5m" + default: "5m" + - name: useOpentracing + type: bool + required: false + description: Whether to enable opentracing for queries. + example: false + default: false \ No newline at end of file diff --git a/bindings/rocketmq/metadata.yaml b/bindings/rocketmq/metadata.yaml new file mode 100644 index 0000000000..f290e8fbd2 --- /dev/null +++ b/bindings/rocketmq/metadata.yaml @@ -0,0 +1,77 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: rocketmq +version: v1 +status: alpha +title: "Apache RocketMQ" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/ +binding: + output: true + input: true + operations: + - name: create + description: "Send message to RocketMQ topic" + - name: read + description: "Receive messages from RocketMQ topic" +authenticationProfiles: + - title: "Access Key Authentication" + description: | + Authenticate with RocketMQ using access key and secret. + metadata: + - name: accessKey + required: true + description: "The access key for authentication" + example: '"your-access-key"' + - name: secretKey + required: true + sensitive: true + description: "The secret key for authentication" + example: '"your-secret-key"' +metadata: + - name: accessProto + required: false + description: "SDK protocol" + example: '"tcp"' + allowedValues: + - "tcp" + - "tcp-cgo" + - "http" + - name: nameServer + required: false + description: "The RocketMQ name server address" + example: '"localhost:9876"' + - name: endpoint + required: false + description: "The RocketMQ endpoint (for http proto)" + example: '"http://localhost:8080"' + - name: consumerGroup + required: false + description: "Consumer group for RocketMQ subscribers" + example: '"my-consumer-group"' + - name: consumerBatchSize + required: false + description: "Consumer batch size" + example: '10' + - name: consumerThreadNums + required: false + description: "Consumer thread numbers (for tcp-cgo proto)" + example: '4' + - name: instanceId + required: false + description: "RocketMQ namespace" + example: '"my-instance"' + - name: nameServerDomain + required: false + description: "RocketMQ name server domain" + example: '"rocketmq.example.com"' + - name: retries + required: false + description: "Retry times to connect to RocketMQ broker" + example: '3' + - name: topics + required: true + description: "Topics to subscribe (comma-separated for multiple topics)" + example: '"topic1,topic2,topic3"' \ No newline at end of file diff --git a/bindings/rocketmq/settings.go b/bindings/rocketmq/settings.go index de3f7932eb..a2588da2ae 100644 --- a/bindings/rocketmq/settings.go +++ b/bindings/rocketmq/settings.go @@ -62,7 +62,7 @@ type Settings struct { Topics TopicsDelimited `mapstructure:"topics"` } -func (s *Settings) Decode(in interface{}) error { +func (s *Settings) Decode(in any) error { if err := metadata.DecodeMetadata(in, s); err != nil { return fmt.Errorf("decode error: %w", err) } diff --git a/bindings/sftp/metadata.yaml b/bindings/sftp/metadata.yaml new file mode 100644 index 0000000000..d7ebce6781 --- /dev/null +++ b/bindings/sftp/metadata.yaml @@ -0,0 +1,80 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: sftp +version: v1 +status: alpha +title: "Secure File Transfer Protocol (SFTP)" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/sftp/ +binding: + output: true + input: false + operations: + - name: create + description: "Upload file via SFTP" + - name: get + description: "Download file from SFTP" + - name: delete + description: "Delete file from SFTP" + - name: list + description: "List files in SFTP directory" +authenticationProfiles: + - title: "Password Authentication" + description: | + Authenticate using username and password. + metadata: + - name: username + required: true + description: "The SFTP username" + example: '"sftpuser"' + - name: password + required: true + sensitive: true + description: "The SFTP password" + example: '"your-password"' + - title: "Private Key Authentication" + description: | + Authenticate using username and private key. + metadata: + - name: username + required: true + description: "The SFTP username" + example: '"sftpuser"' + - name: privateKey + required: true + sensitive: true + description: "The private key for authentication" + example: '"-----BEGIN OPENSSH PRIVATE KEY-----\n..."' + - name: privateKeyPassphrase + required: false + sensitive: true + description: "The passphrase for the private key" + example: '"your-passphrase"' +metadata: + - name: address + required: true + description: "The SFTP server address (host:port)" + example: '"sftp.example.com:22"' + - name: rootPath + required: true + description: "The root directory path on the SFTP server" + example: '"/home/sftpuser"' + - name: fileName + required: false + description: "The file name (can be overridden in request metadata)" + example: '"data.txt"' + - name: hostPublicKey + required: false + description: "The host public key for verification" + example: '"ssh-rsa AAAAB3NzaC1yc2E..."' + - name: knownHostsFile + required: false + description: "Path to the known_hosts file" + example: '"/path/to/known_hosts"' + - name: insecureIgnoreHostKey + required: false + description: "Skip host key verification (insecure)" + example: 'false' + default: 'false' \ No newline at end of file diff --git a/bindings/smtp/metadata.yaml b/bindings/smtp/metadata.yaml new file mode 100644 index 0000000000..e821b394e7 --- /dev/null +++ b/bindings/smtp/metadata.yaml @@ -0,0 +1,70 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: smtp +version: v1 +status: alpha +title: "SMTP" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/smtp/ +binding: + output: true + input: false + operations: + - name: create + description: "Send email via SMTP" +authenticationProfiles: + - title: "User/Password Authentication" + description: | + Authenticate with SMTP server using username and password. + metadata: + - name: user + required: true + description: "The SMTP username" + example: '"user@gmail.com"' + - name: password + required: true + sensitive: true + description: "The SMTP password" + example: '"your-password"' +metadata: + - name: host + required: true + description: "The SMTP server host" + example: '"smtp.gmail.com"' + - name: port + required: false + description: "The SMTP server port" + example: '587' + default: '587' + - name: emailFrom + required: true + description: "The sender email address" + example: '"sender@example.com"' + - name: emailTo + required: true + description: "The recipient email address" + example: '"recipient@example.com"' + - name: emailCC + required: false + description: "The email CC address" + example: '"cc@example.com"' + - name: emailBCC + required: false + description: "The email BCC address" + example: '"bcc@example.com"' + - name: priority + required: false + description: "The email priority" + example: '3' + default: '3' + - name: subject + required: true + description: "The email subject" + example: '"Hello from Dapr"' + - name: skipTLSVerify + required: false + description: "Skip TLS verification" + example: 'false' + default: 'false' \ No newline at end of file diff --git a/bindings/twilio/sendgrid/metadata.yaml b/bindings/twilio/sendgrid/metadata.yaml new file mode 100644 index 0000000000..f7873eeb83 --- /dev/null +++ b/bindings/twilio/sendgrid/metadata.yaml @@ -0,0 +1,55 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: twilio.sendgrid +version: v1 +status: alpha +title: "Twilio SendGrid" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/sendgrid/ +binding: + output: true + input: false + operations: + - name: create + description: "Send email via SendGrid" +authenticationProfiles: + - title: "API Key Authentication" + description: | + Authenticate using SendGrid API key. + metadata: + - name: apiKey + required: true + sensitive: true + description: "The SendGrid API key" + example: '"SG.your-api-key"' +metadata: + - name: emailFrom + required: true + description: "The sender email address" + example: '"sender@example.com"' + - name: emailTo + required: true + description: "The recipient email address" + example: '"recipient@example.com"' + - name: subject + required: true + description: "The email subject" + example: '"Hello from Dapr"' + - name: emailCc + required: false + description: "The CC email address" + example: '"cc@example.com"' + - name: emailBcc + required: false + description: "The BCC email address" + example: '"bcc@example.com"' + - name: dynamicTemplateData + required: false + description: "The dynamic template data" + example: '"{"name":"John","age":30}"' + - name: dynamicTemplateId + required: false + description: "The dynamic template ID" + example: '"your-template-id"' \ No newline at end of file diff --git a/bindings/twilio/sms/metadata.yaml b/bindings/twilio/sms/metadata.yaml new file mode 100644 index 0000000000..61db57040f --- /dev/null +++ b/bindings/twilio/sms/metadata.yaml @@ -0,0 +1,45 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: twilio.sms +version: v1 +status: stable +title: "Twilio SMS" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/twilio/ +binding: + output: true + input: false + operations: + - name: create + description: "Send SMS via Twilio" +authenticationProfiles: + - title: "Twilio Authentication" + description: | + Authenticate using Twilio account credentials. + metadata: + - name: accountSid + required: true + description: "The Twilio account SID" + example: '"AC1234567890abcdef"' + - name: authToken + required: true + sensitive: true + description: "The Twilio auth token" + example: '"your-auth-token"' +metadata: + - name: fromNumber + required: true + description: "The sender phone number" + example: '"+1234567890"' + - name: toNumber + required: true + description: "The recipient phone number" + example: '"+0987654321"' + - name: timeout + required: false + type: duration + description: "The timeout for the SMS request" + example: '30s' + default: '30s' \ No newline at end of file diff --git a/bindings/twilio/sms/sms.go b/bindings/twilio/sms/sms.go index 2bd4a59609..42503d4ce3 100644 --- a/bindings/twilio/sms/sms.go +++ b/bindings/twilio/sms/sms.go @@ -33,10 +33,6 @@ import ( const ( toNumber = "toNumber" - fromNumber = "fromNumber" - accountSid = "accountSid" - authToken = "authToken" - timeout = "timeout" twilioURLBase = "https://api.twilio.com/2010-04-01/Accounts/" ) diff --git a/bindings/wasm/metadata.yaml b/bindings/wasm/metadata.yaml new file mode 100644 index 0000000000..b2ad1b025c --- /dev/null +++ b/bindings/wasm/metadata.yaml @@ -0,0 +1,26 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: bindings +name: wasm +version: v1 +status: alpha +title: "WebAssembly (WASM)" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-bindings/wasm/ +binding: + output: true + input: false + operations: + - name: create + description: "Execute WASM function" +metadata: + - name: strictSandbox + required: false + description: "Strict sandbox mode. When true, uses fake sources to avoid vulnerabilities such as timing attacks." + example: 'true' + default: 'false' + - name: url + required: true + description: "The URL of the WASM file" + example: '"https://example.com/function.wasm"' \ No newline at end of file From 46396406c097055152c5999d035fbbfbe38e60bd Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Thu, 7 Aug 2025 12:12:59 -0500 Subject: [PATCH 06/32] feat: add crypto, lock, nameresolution comps to my setup and fix them up Signed-off-by: Samantha Coyle --- .../cmd/cmd-check-component-registrations.go | 36 +++- .../azure/keyvault/algorithms.go | 0 .../azure/keyvault/component.go | 2 +- .../azure/keyvault/jwk.go | 2 +- .../azure/keyvault/metadata.go | 2 +- cryptography/azure/keyvault/metadata.yaml | 28 +++ {crypto => cryptography}/feature.go | 0 {crypto => cryptography}/jwks/component.go | 2 +- {crypto => cryptography}/jwks/metadata.go | 2 +- cryptography/jwks/metadata.yaml | 35 ++++ {crypto => cryptography}/key.go | 0 .../kubernetes/secrets/component.go | 2 +- .../kubernetes/secrets/metadata.go | 2 +- cryptography/kubernetes/secrets/metadata.yaml | 25 +++ .../local_base_component.go | 0 .../localstorage/component.go | 2 +- .../localstorage/metadata.go | 2 +- cryptography/localstorage/metadata.yaml | 18 ++ {crypto => cryptography}/metadata.go | 0 {crypto => cryptography}/pubkey_cache.go | 0 {crypto => cryptography}/pubkey_cache_test.go | 0 {crypto => cryptography}/subtlecrypto.go | 3 +- lock/redis/metadata.yaml | 152 +++++++++++++++ .../{ => hashicorp}/consul/README.md | 0 .../{ => hashicorp}/consul/configuration.go | 2 +- .../{ => hashicorp}/consul/consul.go | 0 .../{ => hashicorp}/consul/consul_test.go | 0 nameresolution/hashicorp/consul/metadata.yaml | 179 ++++++++++++++++++ .../{ => hashicorp}/consul/watcher.go | 0 nameresolution/kubernetes/kubernetes.go | 2 +- nameresolution/kubernetes/kubernetes_test.go | 6 +- nameresolution/kubernetes/metadata.yaml | 11 ++ nameresolution/mdns/metadata.yaml | 11 ++ nameresolution/sqlite/metadata.yaml | 11 ++ tests/conformance/crypto_test.go | 10 +- .../{crypto => cryptography}/crypto.go | 2 +- .../{crypto => cryptography}/helpers.go | 0 37 files changed, 525 insertions(+), 24 deletions(-) rename {crypto => cryptography}/azure/keyvault/algorithms.go (100%) rename {crypto => cryptography}/azure/keyvault/component.go (99%) rename {crypto => cryptography}/azure/keyvault/jwk.go (98%) rename {crypto => cryptography}/azure/keyvault/metadata.go (97%) create mode 100644 cryptography/azure/keyvault/metadata.yaml rename {crypto => cryptography}/feature.go (100%) rename {crypto => cryptography}/jwks/component.go (98%) rename {crypto => cryptography}/jwks/metadata.go (97%) create mode 100644 cryptography/jwks/metadata.yaml rename {crypto => cryptography}/key.go (100%) rename {crypto => cryptography}/kubernetes/secrets/component.go (98%) rename {crypto => cryptography}/kubernetes/secrets/metadata.go (95%) create mode 100644 cryptography/kubernetes/secrets/metadata.yaml rename {crypto => cryptography}/local_base_component.go (100%) rename {crypto => cryptography}/localstorage/component.go (98%) rename {crypto => cryptography}/localstorage/metadata.go (96%) create mode 100644 cryptography/localstorage/metadata.yaml rename {crypto => cryptography}/metadata.go (100%) rename {crypto => cryptography}/pubkey_cache.go (100%) rename {crypto => cryptography}/pubkey_cache_test.go (100%) rename {crypto => cryptography}/subtlecrypto.go (99%) create mode 100644 lock/redis/metadata.yaml rename nameresolution/{ => hashicorp}/consul/README.md (100%) rename nameresolution/{ => hashicorp}/consul/configuration.go (99%) rename nameresolution/{ => hashicorp}/consul/consul.go (100%) rename nameresolution/{ => hashicorp}/consul/consul_test.go (100%) create mode 100644 nameresolution/hashicorp/consul/metadata.yaml rename nameresolution/{ => hashicorp}/consul/watcher.go (100%) create mode 100644 nameresolution/kubernetes/metadata.yaml create mode 100644 nameresolution/mdns/metadata.yaml create mode 100644 nameresolution/sqlite/metadata.yaml rename tests/conformance/{crypto => cryptography}/crypto.go (99%) rename tests/conformance/{crypto => cryptography}/helpers.go (100%) diff --git a/.build-tools/cmd/cmd-check-component-registrations.go b/.build-tools/cmd/cmd-check-component-registrations.go index ecf37c866f..9e30ddc30f 100644 --- a/.build-tools/cmd/cmd-check-component-registrations.go +++ b/.build-tools/cmd/cmd-check-component-registrations.go @@ -51,8 +51,12 @@ This is a required step before an official Dapr release.`, checkPubSubComponents() checkSecretStoreComponents() checkBindingComponents() + checkConfigurationComponents() + checkLockComponents() + checkCryptographyComponents() + checkNameResolutionComponents() - // TODO: config + // TODO: name resolution, middleware fmt.Println("\nCheck completed!") }, @@ -99,6 +103,32 @@ func checkBindingComponents() { checkComponents("bindings", ignoreDaprComponents, ignoreContribComponents) } +func checkConfigurationComponents() { + fmt.Println("\nChecking configuration components...") + ignoreDaprComponents := []string{"postgresql"} + checkComponents("configuration", ignoreDaprComponents, []string{}) +} + +func checkLockComponents() { + fmt.Println("\nChecking lock components...") + checkComponents("lock", []string{}, []string{}) +} + +func checkCryptographyComponents() { + fmt.Println("\nChecking cryptography components...") + // below is not actually a component. Cryptography section in contrib needs quite a bit of clean up/organization to clean this up so I don't have to "ignore" it. + ignoreContribComponents := []string{"pubkey_cache"} + // TODO: in future rm the aliases with the dapr prefix in runtime to clean this up! + ignoreDaprComponents := []string{"dapr.localstorage", "dapr.kubernetes.secrets", "dapr.jwks"} + checkComponents("cryptography", ignoreDaprComponents, ignoreContribComponents) +} + +func checkNameResolutionComponents() { + fmt.Println("\nChecking name resolution components...") + ignoreDaprComponents := []string{} + checkComponents("nameresolution", ignoreDaprComponents, []string{}) +} + // Note: because this cli cmd changes to the working directory to the root of the repo so pathing is relative to that. func checkComponents(componentType string, ignoreDaprComponents []string, ignoreContribComponents []string) { if err := checkRegistry(componentType); err != nil { @@ -170,7 +200,7 @@ func checkRegistry(componentType string) error { // Check for RegisterComponent() if componentType != "bindings" { - registerComponentCmd := exec.Command("grep", "-r", `Registry) RegisterComponent(componentFactory func(logger.Logger)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) + registerComponentCmd := exec.Command("grep", "-r", `Registry) RegisterComponent(componentFactory`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) _, err = registerComponentCmd.Output() if err != nil { return fmt.Errorf("could not find RegisterComponent method: %v", err) @@ -224,6 +254,8 @@ func findComponentsInBothRepos(componentType string, ignoreContribComponents []s excludeFiles = []string{"--exclude=envelope.go", "--exclude=responses.go"} case "bindings": excludeFiles = []string{"--exclude=client.go"} + case "cryptography": + excludeFiles = []string{"--exclude=key.go", "--exclude=pubkey_cache.go"} default: excludeFiles = []string{} } diff --git a/crypto/azure/keyvault/algorithms.go b/cryptography/azure/keyvault/algorithms.go similarity index 100% rename from crypto/azure/keyvault/algorithms.go rename to cryptography/azure/keyvault/algorithms.go diff --git a/crypto/azure/keyvault/component.go b/cryptography/azure/keyvault/component.go similarity index 99% rename from crypto/azure/keyvault/component.go rename to cryptography/azure/keyvault/component.go index c3efb00cf2..537696db75 100644 --- a/crypto/azure/keyvault/component.go +++ b/cryptography/azure/keyvault/component.go @@ -26,7 +26,7 @@ import ( "github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jwk" - contribCrypto "github.com/dapr/components-contrib/crypto" + contribCrypto "github.com/dapr/components-contrib/cryptography" contribMetadata "github.com/dapr/components-contrib/metadata" internals "github.com/dapr/kit/crypto" "github.com/dapr/kit/logger" diff --git a/crypto/azure/keyvault/jwk.go b/cryptography/azure/keyvault/jwk.go similarity index 98% rename from crypto/azure/keyvault/jwk.go rename to cryptography/azure/keyvault/jwk.go index eda8325b8a..d663b09035 100644 --- a/crypto/azure/keyvault/jwk.go +++ b/cryptography/azure/keyvault/jwk.go @@ -25,7 +25,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys" "github.com/lestrrat-go/jwx/v2/jwk" - contribCrypto "github.com/dapr/components-contrib/crypto" + contribCrypto "github.com/dapr/components-contrib/cryptography" ) // KeyBundleToKey converts an azkeys.KeyBundle object to a contribCrypto.Key object, containing only the public part of the asymmetric key. diff --git a/crypto/azure/keyvault/metadata.go b/cryptography/azure/keyvault/metadata.go similarity index 97% rename from crypto/azure/keyvault/metadata.go rename to cryptography/azure/keyvault/metadata.go index b5d48cc529..0ef3350be1 100644 --- a/crypto/azure/keyvault/metadata.go +++ b/cryptography/azure/keyvault/metadata.go @@ -20,7 +20,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore" azauth "github.com/dapr/components-contrib/common/authentication/azure" - contribCrypto "github.com/dapr/components-contrib/crypto" + contribCrypto "github.com/dapr/components-contrib/cryptography" "github.com/dapr/kit/metadata" ) diff --git a/cryptography/azure/keyvault/metadata.yaml b/cryptography/azure/keyvault/metadata.yaml new file mode 100644 index 0000000000..390500d579 --- /dev/null +++ b/cryptography/azure/keyvault/metadata.yaml @@ -0,0 +1,28 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: cryptography +name: azure.keyvault +version: v1 +status: alpha +title: "Azure Key Vault" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-cryptography/azure-key-vault/ +builtinAuthenticationProfiles: + - name: "azuread" +metadata: + - name: vaultName + type: string + required: true + description: | + Name of the Azure Key Vault resource. + example: "my-key-vault" + - name: requestTimeout + type: duration + required: false + description: | + Timeout for network requests, as a Go duration string. + example: "30s" + default: "30s" + + diff --git a/crypto/feature.go b/cryptography/feature.go similarity index 100% rename from crypto/feature.go rename to cryptography/feature.go diff --git a/crypto/jwks/component.go b/cryptography/jwks/component.go similarity index 98% rename from crypto/jwks/component.go rename to cryptography/jwks/component.go index 42a18d11d9..ffdc76bb7f 100644 --- a/crypto/jwks/component.go +++ b/cryptography/jwks/component.go @@ -23,7 +23,7 @@ import ( "github.com/lestrrat-go/jwx/v2/jwk" - contribCrypto "github.com/dapr/components-contrib/crypto" + contribCrypto "github.com/dapr/components-contrib/cryptography" contribMetadata "github.com/dapr/components-contrib/metadata" "github.com/dapr/kit/jwkscache" "github.com/dapr/kit/logger" diff --git a/crypto/jwks/metadata.go b/cryptography/jwks/metadata.go similarity index 97% rename from crypto/jwks/metadata.go rename to cryptography/jwks/metadata.go index da384f568c..29a0181424 100644 --- a/crypto/jwks/metadata.go +++ b/cryptography/jwks/metadata.go @@ -17,7 +17,7 @@ import ( "errors" "time" - contribCrypto "github.com/dapr/components-contrib/crypto" + contribCrypto "github.com/dapr/components-contrib/cryptography" "github.com/dapr/kit/metadata" ) diff --git a/cryptography/jwks/metadata.yaml b/cryptography/jwks/metadata.yaml new file mode 100644 index 0000000000..8ba1bdc11d --- /dev/null +++ b/cryptography/jwks/metadata.yaml @@ -0,0 +1,35 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: cryptography +name: jwks +version: v1 +status: alpha +title: "JWKS" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-cryptography/json-web-key-sets/ +metadata: + - name: jwks + type: string + required: true + description: | + The JWKS to use. Can be one of: + - The actual JWKS as a JSON-encoded string (optionally encoded with Base64-standard). + - A URL to a HTTP(S) endpoint returning the JWKS. + - A path to a local file containing the JWKS. + example: '"https://example.com/.well-known/jwks.json"' + - name: requestTimeout + type: duration + required: false + description: | + Timeout for network requests, as a Go duration string. + example: "30s" + default: "30s" + - name: minRefreshInterval + type: duration + required: false + description: | + Minimum interval before the JWKS is refreshed, as a Go duration string. + Only applies when the JWKS is fetched from a HTTP(S) URL. + example: "10m" + default: "10m" diff --git a/crypto/key.go b/cryptography/key.go similarity index 100% rename from crypto/key.go rename to cryptography/key.go diff --git a/crypto/kubernetes/secrets/component.go b/cryptography/kubernetes/secrets/component.go similarity index 98% rename from crypto/kubernetes/secrets/component.go rename to cryptography/kubernetes/secrets/component.go index f6d2573470..06a99ab9ea 100644 --- a/crypto/kubernetes/secrets/component.go +++ b/cryptography/kubernetes/secrets/component.go @@ -28,7 +28,7 @@ import ( "k8s.io/client-go/kubernetes" kubeclient "github.com/dapr/components-contrib/common/authentication/kubernetes" - contribCrypto "github.com/dapr/components-contrib/crypto" + contribCrypto "github.com/dapr/components-contrib/cryptography" "github.com/dapr/components-contrib/metadata" internals "github.com/dapr/kit/crypto" "github.com/dapr/kit/logger" diff --git a/crypto/kubernetes/secrets/metadata.go b/cryptography/kubernetes/secrets/metadata.go similarity index 95% rename from crypto/kubernetes/secrets/metadata.go rename to cryptography/kubernetes/secrets/metadata.go index fc9471487a..c007c6636a 100644 --- a/crypto/kubernetes/secrets/metadata.go +++ b/cryptography/kubernetes/secrets/metadata.go @@ -14,7 +14,7 @@ limitations under the License. package secrets import ( - contribCrypto "github.com/dapr/components-contrib/crypto" + contribCrypto "github.com/dapr/components-contrib/cryptography" "github.com/dapr/kit/metadata" ) diff --git a/cryptography/kubernetes/secrets/metadata.yaml b/cryptography/kubernetes/secrets/metadata.yaml new file mode 100644 index 0000000000..1e3d9b5e9b --- /dev/null +++ b/cryptography/kubernetes/secrets/metadata.yaml @@ -0,0 +1,25 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: cryptography +name: kubernetes.secrets +version: v1 +status: alpha +title: "Kubernetes Secrets" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-cryptography/kubernetes-secrets/ +metadata: + - name: defaultNamespace + type: string + required: false + description: | + Default namespace to retrieve secrets from. + If unset, the namespace must be specified for each key, as `namespace/secretName/key`. + example: '"default"' + - name: kubeconfigPath + type: string + required: false + description: | + Path to a kubeconfig file. + If empty, uses the default values. + example: '"/path/to/kubeconfig"' diff --git a/crypto/local_base_component.go b/cryptography/local_base_component.go similarity index 100% rename from crypto/local_base_component.go rename to cryptography/local_base_component.go diff --git a/crypto/localstorage/component.go b/cryptography/localstorage/component.go similarity index 98% rename from crypto/localstorage/component.go rename to cryptography/localstorage/component.go index b633e3678e..c5fea75135 100644 --- a/crypto/localstorage/component.go +++ b/cryptography/localstorage/component.go @@ -25,7 +25,7 @@ import ( "github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jwk" - contribCrypto "github.com/dapr/components-contrib/crypto" + contribCrypto "github.com/dapr/components-contrib/cryptography" contribMetadata "github.com/dapr/components-contrib/metadata" internals "github.com/dapr/kit/crypto" "github.com/dapr/kit/logger" diff --git a/crypto/localstorage/metadata.go b/cryptography/localstorage/metadata.go similarity index 96% rename from crypto/localstorage/metadata.go rename to cryptography/localstorage/metadata.go index f4d5a036f0..de8b4ac319 100644 --- a/crypto/localstorage/metadata.go +++ b/cryptography/localstorage/metadata.go @@ -19,7 +19,7 @@ import ( "os" "path/filepath" - contribCrypto "github.com/dapr/components-contrib/crypto" + contribCrypto "github.com/dapr/components-contrib/cryptography" "github.com/dapr/kit/metadata" ) diff --git a/cryptography/localstorage/metadata.yaml b/cryptography/localstorage/metadata.yaml new file mode 100644 index 0000000000..e7841aefce --- /dev/null +++ b/cryptography/localstorage/metadata.yaml @@ -0,0 +1,18 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: cryptography +name: localstorage +version: v1 +status: alpha +title: "Local Storage" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-cryptography/local-storage/ +metadata: + - name: path + type: string + required: true + description: | + Path to a local folder where keys are stored. + Keys are loaded from PEM or JSON (each containing an individual JWK) files from this folder. + example: '"/path/to/keys"' \ No newline at end of file diff --git a/crypto/metadata.go b/cryptography/metadata.go similarity index 100% rename from crypto/metadata.go rename to cryptography/metadata.go diff --git a/crypto/pubkey_cache.go b/cryptography/pubkey_cache.go similarity index 100% rename from crypto/pubkey_cache.go rename to cryptography/pubkey_cache.go diff --git a/crypto/pubkey_cache_test.go b/cryptography/pubkey_cache_test.go similarity index 100% rename from crypto/pubkey_cache_test.go rename to cryptography/pubkey_cache_test.go diff --git a/crypto/subtlecrypto.go b/cryptography/subtlecrypto.go similarity index 99% rename from crypto/subtlecrypto.go rename to cryptography/subtlecrypto.go index 181e3cf6d2..d3c5d9193e 100644 --- a/crypto/subtlecrypto.go +++ b/cryptography/subtlecrypto.go @@ -17,9 +17,8 @@ import ( "context" "io" - "github.com/lestrrat-go/jwx/v2/jwk" - "github.com/dapr/components-contrib/metadata" + "github.com/lestrrat-go/jwx/v2/jwk" ) // SubtleCrypto offers an interface to perform low-level ("subtle") cryptographic operations with keys stored in a vault. diff --git a/lock/redis/metadata.yaml b/lock/redis/metadata.yaml new file mode 100644 index 0000000000..a32a4901a2 --- /dev/null +++ b/lock/redis/metadata.yaml @@ -0,0 +1,152 @@ +# yaml-language-server: $schema=../../component-metadata-schema.json +schemaVersion: v1 +type: lock +name: redis +version: v1 +status: alpha +title: "Redis Distributed Lock" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-locks/redis-lock/ +lock: + operations: + - name: tryLock + description: "Attempt to acquire a distributed lock" + - name: unlock + description: "Release a distributed lock" +authenticationProfiles: + - title: "Password Authentication" + description: | + Authenticate using Redis password. + metadata: + - name: redisPassword + required: false + sensitive: true + description: "The Redis password" + example: '"your-redis-password"' +metadata: + - name: redisHost + required: true + description: "The Redis host address" + example: '"localhost:6379"' + - name: redisPassword + required: false + sensitive: true + description: "The Redis password" + example: '"your-redis-password"' + - name: redisType + required: false + description: "The Redis type" + example: "node" + default: "node" + allowedValues: + - "node" + - "cluster" + - "sentinel" + - name: redisDB + required: false + description: "The Redis database number" + example: '0' + default: '0' + - name: redisMaxRetries + required: false + description: "Maximum Redis retries" + example: '3' + default: '3' + - name: redisMinRetryInterval + required: false + description: "Minimum Redis retry interval" + example: "8ms" + default: "8ms" + - name: redisMaxRetryInterval + required: false + description: "Maximum Redis retry interval" + example: "512ms" + default: "512ms" + - name: dialTimeout + required: false + description: "Dial timeout duration" + example: "5s" + default: "5s" + - name: readTimeout + required: false + description: "Read timeout duration" + example: "3s" + default: "3s" + - name: writeTimeout + required: false + description: "Write timeout duration" + example: "3s" + default: "3s" + - name: poolSize + required: false + description: "Connection pool size" + example: '10' + default: '10' + - name: poolTimeout + required: false + description: "Connection pool timeout" + example: "4s" + default: "4s" + - name: maxConnAge + required: false + description: "Maximum connection age" + example: "30m" + default: "30m" + - name: minIdleConns + required: false + description: "Minimum idle connections" + example: '0' + default: '0' + - name: idleTimeout + required: false + description: "Idle timeout duration" + example: "5m" + default: "5m" + - name: idleCheckFrequency + required: false + description: "Idle check frequency" + example: "1m" + default: "1m" + - name: maxRetries + required: false + description: "Maximum number of retries when attempting to acquire a lock" + example: '3' + default: '3' + - name: maxRetryBackoff + required: false + description: "Maximum backoff duration between retries" + example: "2s" + default: "2s" + - name: redeliverInterval + required: false + description: "Redeliver interval for re-attempting lock acquisition" + example: "15s" + default: "15s" + - name: processingTimeout + required: false + description: "Processing timeout for lock ownership" + example: "60s" + default: "60s" + - name: enableTLS + required: false + type: bool + description: "Whether to enable TLS encryption" + example: false + default: false + - name: useEntraID + required: false + type: bool + description: "Whether to use Entra ID for authentication" + example: false + default: false + - name: failover + required: false + type: bool + description: "Whether to enable failover mode (for Sentinel)" + example: false + default: false + - name: sentinelMasterName + required: false + description: "The Sentinel master name (used if redisType is 'sentinel')" + example: '"mymaster"' diff --git a/nameresolution/consul/README.md b/nameresolution/hashicorp/consul/README.md similarity index 100% rename from nameresolution/consul/README.md rename to nameresolution/hashicorp/consul/README.md diff --git a/nameresolution/consul/configuration.go b/nameresolution/hashicorp/consul/configuration.go similarity index 99% rename from nameresolution/consul/configuration.go rename to nameresolution/hashicorp/consul/configuration.go index ccf4e58010..15e66ca756 100644 --- a/nameresolution/consul/configuration.go +++ b/nameresolution/hashicorp/consul/configuration.go @@ -60,7 +60,7 @@ func newIntermediateConfig() intermediateConfig { } } -func parseConfig(rawConfig interface{}) (configSpec, error) { +func parseConfig(rawConfig any) (configSpec, error) { var result configSpec rawConfig, err := config.Normalize(rawConfig) if err != nil { diff --git a/nameresolution/consul/consul.go b/nameresolution/hashicorp/consul/consul.go similarity index 100% rename from nameresolution/consul/consul.go rename to nameresolution/hashicorp/consul/consul.go diff --git a/nameresolution/consul/consul_test.go b/nameresolution/hashicorp/consul/consul_test.go similarity index 100% rename from nameresolution/consul/consul_test.go rename to nameresolution/hashicorp/consul/consul_test.go diff --git a/nameresolution/hashicorp/consul/metadata.yaml b/nameresolution/hashicorp/consul/metadata.yaml new file mode 100644 index 0000000000..70d11ee740 --- /dev/null +++ b/nameresolution/hashicorp/consul/metadata.yaml @@ -0,0 +1,179 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: nameresolution +name: hashicorp.consul +version: v1 +status: alpha +title: "HashiCorp Consul" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-name-resolution/setup-nr-consul/ +authenticationProfiles: + - title: "Token Authentication" + description: "Connect to Consul using a token for authentication." + metadata: + - name: token + type: string + required: true + description: The Consul ACL token for authentication. + sensitive: true + example: "your-consul-token" + - title: "Username/Password Authentication" + description: "Connect to Consul using a username and password." + metadata: + - name: username + type: string + required: false + description: Username for HTTP basic authentication. + example: "username" + - name: password + type: string + required: false + description: Password for HTTP basic authentication. + sensitive: true + example: "password" + - title: "TLS Authentication" + description: "Connect to Consul using TLS encryption with certificates." + metadata: + - name: tlsAddress + type: string + required: true + description: The TLS address of the Consul server. + example: "localhost:8501" + - name: caFile + type: string + required: true + description: Path to the CA certificate file. + example: "/path/to/ca.crt" + - name: certFile + type: string + required: true + description: Path to the client certificate file. + example: "/path/to/client.crt" + - name: keyFile + type: string + required: true + description: Path to the client private key file. + example: "/path/to/client.key" + - name: insecureSkipVerify + type: boolean + required: false + description: Skip TLS certificate verification. + example: false + default: false +metadata: + - name: address + type: string + required: true + description: The address of the Consul server. + example: "localhost:8500" + - name: scheme + type: string + required: false + description: The scheme to use for connecting to Consul (http or https). + example: "https" + default: "http" + - name: datacenter + type: string + required: false + description: The Consul datacenter to use. + example: "dc1" + - name: waitTime + type: string + required: false + description: The wait time for Consul operations. + example: "10s" + - name: tokenFile + type: string + required: false + description: Path to a file containing the Consul ACL token. + example: "/path/to/token" + - name: daprPortMetaKey + type: string + required: false + description: The metadata key used to store the Dapr port. + example: "DAPR_PORT" + default: "DAPR_PORT" + - name: selfRegister + type: boolean + required: false + description: Whether to register this service with Consul. + example: true + default: false + - name: selfDeregister + type: boolean + required: false + description: Whether to deregister this service from Consul on shutdown. + example: true + default: false + - name: useCache + type: boolean + required: false + description: Whether to use caching for service lookups. + example: true + default: false + - name: tags + type: array + required: false + description: Tags to associate with the service registration. + example: ["dapr", "v1"] + - name: meta + type: object + required: false + description: Metadata to associate with the service registration. + example: | + { + "version": "v1", + "environment": "production" + } + - name: checks + type: array + required: false + description: Health checks to associate with the service registration. + example: | + [ + { + "name": "dapr-health", + "http": "http://localhost:3500/health", + "interval": "10s", + "timeout": "5s" + } + ] + - name: namespace + type: string + required: false + description: The Consul namespace to use for queries. + example: "default" + - name: partition + type: string + required: false + description: The Consul partition to use for queries. + example: "default" + - name: queryDatacenter + type: string + required: false + description: The Consul datacenter to use for queries. + example: "dc1" + - name: queryToken + type: string + required: false + description: The Consul ACL token to use for queries. + sensitive: true + example: "query-token" + - name: queryWaitTime + type: string + required: false + description: The wait time for Consul queries. + example: "5s" + - name: allowStale + type: boolean + required: false + description: Whether to allow stale results in queries. + example: true + default: false + - name: requireConsistent + type: boolean + required: false + description: Whether to require consistent reads in queries. + example: false + default: false diff --git a/nameresolution/consul/watcher.go b/nameresolution/hashicorp/consul/watcher.go similarity index 100% rename from nameresolution/consul/watcher.go rename to nameresolution/hashicorp/consul/watcher.go diff --git a/nameresolution/kubernetes/kubernetes.go b/nameresolution/kubernetes/kubernetes.go index f8233ef72c..14ff0cb255 100644 --- a/nameresolution/kubernetes/kubernetes.go +++ b/nameresolution/kubernetes/kubernetes.go @@ -69,7 +69,7 @@ func (k *resolver) Init(ctx context.Context, metadata nameresolution.Metadata) e return err } - if cfg, ok := configInterface.(map[string]interface{}); ok { + if cfg, ok := configInterface.(map[string]any); ok { clusterDomainAny := cfg[ClusterDomainKey] tmplStrAny := cfg[TemplateKey] diff --git a/nameresolution/kubernetes/kubernetes_test.go b/nameresolution/kubernetes/kubernetes_test.go index d31b175999..7eec4a7ec9 100644 --- a/nameresolution/kubernetes/kubernetes_test.go +++ b/nameresolution/kubernetes/kubernetes_test.go @@ -37,7 +37,7 @@ func TestResolve(t *testing.T) { func TestResolveWithCustomClusterDomain(t *testing.T) { resolver := NewResolver(logger.NewLogger("test")) _ = resolver.Init(t.Context(), nameresolution.Metadata{ - Configuration: map[string]interface{}{ + Configuration: map[string]any{ "clusterDomain": "mydomain.com", }, }) @@ -53,7 +53,7 @@ func TestResolveWithCustomClusterDomain(t *testing.T) { func TestResolveWithTemplate(t *testing.T) { resolver := NewResolver(logger.NewLogger("test")) _ = resolver.Init(t.Context(), nameresolution.Metadata{ - Configuration: map[string]interface{}{ + Configuration: map[string]any{ "template": "{{.ID}}-{{.Namespace}}.internal:{{.Port}}", }, }) @@ -69,7 +69,7 @@ func TestResolveWithTemplate(t *testing.T) { func TestResolveWithTemplateAndData(t *testing.T) { resolver := NewResolver(logger.NewLogger("test")) _ = resolver.Init(t.Context(), nameresolution.Metadata{ - Configuration: map[string]interface{}{ + Configuration: map[string]any{ "template": "{{.ID}}-{{.Data.region}}.internal:{{.Port}}", }, }) diff --git a/nameresolution/kubernetes/metadata.yaml b/nameresolution/kubernetes/metadata.yaml new file mode 100644 index 0000000000..3f7ae02834 --- /dev/null +++ b/nameresolution/kubernetes/metadata.yaml @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: nameresolution +name: kubernetes +version: v1 +status: stable +title: "Kubernetes" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-name-resolution/nr-kubernetes/ +metadata: [] \ No newline at end of file diff --git a/nameresolution/mdns/metadata.yaml b/nameresolution/mdns/metadata.yaml new file mode 100644 index 0000000000..54374bbf38 --- /dev/null +++ b/nameresolution/mdns/metadata.yaml @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: nameresolution +name: mdns +version: v1 +status: stable +title: "mDNS" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-name-resolution/nr-mdns/ +metadata: [] \ No newline at end of file diff --git a/nameresolution/sqlite/metadata.yaml b/nameresolution/sqlite/metadata.yaml new file mode 100644 index 0000000000..8ebdf61c4a --- /dev/null +++ b/nameresolution/sqlite/metadata.yaml @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: nameresolution +name: sqlite +version: v1 +status: stable +title: "SQLite" +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-name-resolution/nr-sqlite/ +metadata: [] diff --git a/tests/conformance/crypto_test.go b/tests/conformance/crypto_test.go index 749c6beb5a..618b9ba8dd 100644 --- a/tests/conformance/crypto_test.go +++ b/tests/conformance/crypto_test.go @@ -22,11 +22,11 @@ import ( "github.com/stretchr/testify/require" - contribCrypto "github.com/dapr/components-contrib/crypto" - cr_azurekeyvault "github.com/dapr/components-contrib/crypto/azure/keyvault" - cr_jwks "github.com/dapr/components-contrib/crypto/jwks" - cr_localstorage "github.com/dapr/components-contrib/crypto/localstorage" - conf_crypto "github.com/dapr/components-contrib/tests/conformance/crypto" + contribCrypto "github.com/dapr/components-contrib/cryptography" + cr_azurekeyvault "github.com/dapr/components-contrib/cryptography/azure/keyvault" + cr_jwks "github.com/dapr/components-contrib/cryptography/jwks" + cr_localstorage "github.com/dapr/components-contrib/cryptography/localstorage" + conf_crypto "github.com/dapr/components-contrib/tests/conformance/cryptography" ) func TestCryptoConformance(t *testing.T) { diff --git a/tests/conformance/crypto/crypto.go b/tests/conformance/cryptography/crypto.go similarity index 99% rename from tests/conformance/crypto/crypto.go rename to tests/conformance/cryptography/crypto.go index 260e9b179d..377d17e19d 100644 --- a/tests/conformance/crypto/crypto.go +++ b/tests/conformance/cryptography/crypto.go @@ -26,7 +26,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - contribCrypto "github.com/dapr/components-contrib/crypto" + contribCrypto "github.com/dapr/components-contrib/cryptography" "github.com/dapr/components-contrib/metadata" "github.com/dapr/components-contrib/tests/conformance/utils" "github.com/dapr/kit/config" diff --git a/tests/conformance/crypto/helpers.go b/tests/conformance/cryptography/helpers.go similarity index 100% rename from tests/conformance/crypto/helpers.go rename to tests/conformance/cryptography/helpers.go From 204f64f08ed69e7d1839f0dfba8b1d46e07e708a Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Thu, 7 Aug 2025 13:05:26 -0500 Subject: [PATCH 07/32] style: rename file to what it should be Signed-off-by: Samantha Coyle --- .../{routeralias_middleware.go => routeralias_alias.go} | 0 .../{routeralias_middleware_test.go => routeralias_alias_test.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename middleware/http/routeralias/{routeralias_middleware.go => routeralias_alias.go} (100%) rename middleware/http/routeralias/{routeralias_middleware_test.go => routeralias_alias_test.go} (100%) diff --git a/middleware/http/routeralias/routeralias_middleware.go b/middleware/http/routeralias/routeralias_alias.go similarity index 100% rename from middleware/http/routeralias/routeralias_middleware.go rename to middleware/http/routeralias/routeralias_alias.go diff --git a/middleware/http/routeralias/routeralias_middleware_test.go b/middleware/http/routeralias/routeralias_alias_test.go similarity index 100% rename from middleware/http/routeralias/routeralias_middleware_test.go rename to middleware/http/routeralias/routeralias_alias_test.go From c76ce884089b16fb1591ba1952fbd6fd8199d1a2 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Thu, 7 Aug 2025 13:35:59 -0500 Subject: [PATCH 08/32] feat: add middleware components to cli too Signed-off-by: Samantha Coyle --- .../cmd/cmd-check-component-registrations.go | 81 +++++++++++++---- middleware/http/bearer/metadata.yaml | 29 ++++++ middleware/http/oauth2/metadata.yaml | 66 ++++++++++++++ .../oauth2clientcredentials/metadata.yaml | 64 +++++++++++++ middleware/http/opa/metadata.yaml | 65 ++++++++++++++ middleware/http/ratelimit/metadata.yaml | 19 ++++ middleware/http/routerchecker/metadata.yaml | 18 ++++ middleware/http/sentinel/metadata.yaml | 90 +++++++++++++++++++ middleware/http/wasm/metadata.yaml | 27 ++++++ nameresolution/sqlite/metadata.yaml | 2 +- 10 files changed, 445 insertions(+), 16 deletions(-) create mode 100644 middleware/http/bearer/metadata.yaml create mode 100644 middleware/http/oauth2/metadata.yaml create mode 100644 middleware/http/oauth2clientcredentials/metadata.yaml create mode 100644 middleware/http/opa/metadata.yaml create mode 100644 middleware/http/ratelimit/metadata.yaml create mode 100644 middleware/http/routerchecker/metadata.yaml create mode 100644 middleware/http/sentinel/metadata.yaml create mode 100644 middleware/http/wasm/metadata.yaml diff --git a/.build-tools/cmd/cmd-check-component-registrations.go b/.build-tools/cmd/cmd-check-component-registrations.go index 9e30ddc30f..8bbc88841a 100644 --- a/.build-tools/cmd/cmd-check-component-registrations.go +++ b/.build-tools/cmd/cmd-check-component-registrations.go @@ -55,8 +55,7 @@ This is a required step before an official Dapr release.`, checkLockComponents() checkCryptographyComponents() checkNameResolutionComponents() - - // TODO: name resolution, middleware + checkMiddlewareComponents() fmt.Println("\nCheck completed!") }, @@ -125,8 +124,16 @@ func checkCryptographyComponents() { func checkNameResolutionComponents() { fmt.Println("\nChecking name resolution components...") - ignoreDaprComponents := []string{} - checkComponents("nameresolution", ignoreDaprComponents, []string{}) + checkComponents("nameresolution", []string{}, []string{}) +} + +func checkMiddlewareComponents() { + fmt.Println("\nChecking middleware components...") + // uppercase is a component only in runtime which doesn't make sense give the nameresolution components have no real metadata of their own for the most part; + // however, it's definition is only in runtime and not in contrib so we must ignore it. + ignoreDaprComponents := []string{"uppercase"} + ignoreContribComponents := []string{} + checkComponents("middleware", ignoreDaprComponents, ignoreContribComponents) } // Note: because this cli cmd changes to the working directory to the root of the repo so pathing is relative to that. @@ -190,9 +197,18 @@ func checkComponents(componentType string, ignoreDaprComponents []string, ignore reportResults(missingRegistrations, missingBuildTags, missingMetadata, componentType) } +func getRegistryPath(componentType string) string { + if componentType == "middleware" { + return fmt.Sprintf("../dapr/pkg/components/%s/http/registry.go", componentType) + } + return fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType) +} + func checkRegistry(componentType string) error { + registryPath := getRegistryPath(componentType) + // Check for default registry singleton - registryCmd := exec.Command("grep", "-r", `var DefaultRegistry \*Registry = NewRegistry\(\)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) + registryCmd := exec.Command("grep", "-r", `var DefaultRegistry \*Registry = NewRegistry\(\)`, registryPath) _, err := registryCmd.Output() if err != nil { return fmt.Errorf("could not find default registry: %v", err) @@ -200,39 +216,39 @@ func checkRegistry(componentType string) error { // Check for RegisterComponent() if componentType != "bindings" { - registerComponentCmd := exec.Command("grep", "-r", `Registry) RegisterComponent(componentFactory`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) - _, err = registerComponentCmd.Output() + registerComponentCmd := exec.Command("grep", "-r", `Registry) RegisterComponent(componentFactory`, registryPath) + _, err := registerComponentCmd.Output() if err != nil { return fmt.Errorf("could not find RegisterComponent method: %v", err) } // Check for Create() - createCmd := exec.Command("grep", "-r", `Registry) Create(name, version, logName string)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) + createCmd := exec.Command("grep", "-r", `Registry) Create(name, version`, registryPath) _, err = createCmd.Output() if err != nil { return fmt.Errorf("could not find Create method: %v", err) } } else { - registerInputBindingCmd := exec.Command("grep", "-r", `Registry) RegisterInputBinding(componentFactory func(logger.Logger)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) - _, err = registerInputBindingCmd.Output() + registerInputBindingCmd := exec.Command("grep", "-r", `Registry) RegisterInputBinding(componentFactory func(logger.Logger)`, registryPath) + _, err := registerInputBindingCmd.Output() if err != nil { return fmt.Errorf("could not find registerInputBindingCmd method: %v", err) } - registerOutputBindingCmd := exec.Command("grep", "-r", `Registry) RegisterOutputBinding(componentFactory func(logger.Logger)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) + registerOutputBindingCmd := exec.Command("grep", "-r", `Registry) RegisterOutputBinding(componentFactory func(logger.Logger)`, registryPath) _, err = registerOutputBindingCmd.Output() if err != nil { return fmt.Errorf("could not find registerOutputBindingCmd method: %v", err) } // Check for Creates - createInputBindingCmd := exec.Command("grep", "-r", `Registry) CreateInputBinding(name, version, logName string)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) + createInputBindingCmd := exec.Command("grep", "-r", `Registry) CreateInputBinding(name, version, logName string)`, registryPath) _, err = createInputBindingCmd.Output() if err != nil { return fmt.Errorf("could not find CreateInputBinding method: %v", err) } - createOutputBindingCmd := exec.Command("grep", "-r", `Registry) CreateOutputBinding(name, version, logName string)`, fmt.Sprintf("../dapr/pkg/components/%s/registry.go", componentType)) + createOutputBindingCmd := exec.Command("grep", "-r", `Registry) CreateOutputBinding(name, version, logName string)`, registryPath) _, err = createOutputBindingCmd.Output() if err != nil { return fmt.Errorf("could not find CreateInputBinding method: %v", err) @@ -256,6 +272,8 @@ func findComponentsInBothRepos(componentType string, ignoreContribComponents []s excludeFiles = []string{"--exclude=client.go"} case "cryptography": excludeFiles = []string{"--exclude=key.go", "--exclude=pubkey_cache.go"} + case "middleware": + excludeFiles = []string{"--exclude=mock*"} default: excludeFiles = []string{} } @@ -274,7 +292,13 @@ func findComponentsInBothRepos(componentType string, ignoreContribComponents []s // Find all registered components in dapr/dapr var registeredOutput []byte if componentType != "bindings" { - registeredCmd := exec.Command("sh", "-c", fmt.Sprintf(`grep -r "RegisterComponent" ../dapr/cmd/daprd/components/%s_*.go`, componentType)) + var searchPattern string + if componentType == "middleware" { + searchPattern = "../dapr/cmd/daprd/components/middleware_http_*.go" + } else { + searchPattern = fmt.Sprintf("../dapr/cmd/daprd/components/%s_*.go", componentType) + } + registeredCmd := exec.Command("sh", "-c", fmt.Sprintf(`grep -r "RegisterComponent" %s`, searchPattern)) registeredOutput, err = registeredCmd.Output() if err != nil { return nil, nil, fmt.Errorf("could not find all registered components in dapr/dapr: %v", err) @@ -376,7 +400,9 @@ func normalizeComponentName(contrib string) string { } parts := strings.Split(contrib, ".") - vendorPrefixes := []string{"hashicorp", "aws", "azure", "gcp", "alicloud", "oci", "cloudflare", "ibm", "tencentcloud", "huaweicloud", "twilio"} + // Note: I am putting http as a vendor prefix, but that should be removed and all http middleware components should be http.blah bc + // in future we could add grpc middleware components. + vendorPrefixes := []string{"hashicorp", "aws", "azure", "gcp", "alicloud", "oci", "cloudflare", "ibm", "tencentcloud", "huaweicloud", "twilio", "http"} versionSuffixes := []string{"v1", "v2", "internal"} // Handle 2-part names (vendor.component) @@ -447,6 +473,12 @@ func checkMetadataFile(contrib, componentType string) error { } func getMetadataFilePath(contrib, componentType string) string { + // Special handling for HTTP middleware components + // The metadata files are located at middleware/http/componentname/metadata.yaml + if componentType == "middleware" { + return fmt.Sprintf("%s/http/%s/metadata.yaml", componentType, contrib) + } + if strings.Contains(contrib, ".") { // For nested components like "aws.bedrock", split and join with "/" parts := strings.Split(contrib, ".") @@ -531,6 +563,13 @@ func extractComponentNameFromPath(filePath, componentType string) string { // For nested components: aws/bedrock/bedrock.go -> aws.bedrock // Take all parts except the last (which is the filename) dirParts := parts[:len(parts)-1] + + // Special handling for HTTP middleware components + // middleware/http/componentname/ -> componentname (not http.componentname) + if componentType == "middleware" && len(dirParts) >= 2 && dirParts[0] == "http" { + return strings.Join(dirParts[1:], ".") + } + return strings.Join(dirParts, ".") } else if len(parts) == 1 { // For simple components: echo/echo.go -> echo @@ -565,6 +604,13 @@ func getRegistrationFileName(contrib, componentType string) string { } fileName := strings.ReplaceAll(contrib, ".", "_") + + // Special handling for HTTP middleware components + // The registration files are named middleware_http_componentname.go + if componentType == "middleware" { + return fmt.Sprintf("../dapr/cmd/daprd/components/%s_http_%s.go", componentType, fileName) + } + return fmt.Sprintf("../dapr/cmd/daprd/components/%s_%s.go", componentType, fileName) } @@ -589,6 +635,11 @@ func parseRegisteredComponents(output string, componentType string) []string { // Process each file's content for filePath := range fileGroups { + // skip the uppercase component as it has a ton of magic strings that we must ignore. + // all other components only have the component registration string in this file so it is an outlier. + if strings.Contains(filePath, "uppercase") { + continue + } // Read the entire file content cmd := exec.Command("cat", filePath) diff --git a/middleware/http/bearer/metadata.yaml b/middleware/http/bearer/metadata.yaml new file mode 100644 index 0000000000..1e0ee4e45d --- /dev/null +++ b/middleware/http/bearer/metadata.yaml @@ -0,0 +1,29 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: middleware +name: bearer +version: v1 +status: stable +title: "Bearer Token Authentication" +description: | + The Bearer middleware provides JWT token authentication for HTTP requests. + It validates Bearer tokens in the Authorization header and can extract claims for downstream processing. +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-bearer/ +metadata: + - name: jwksURL + type: string + required: true + description: "The URL of the JSON Web Key Set (JWKS) endpoint" + example: "https://accounts.google.com/.well-known/jwks.json" + - name: issuer + type: string + required: true + description: "The expected issuer of the JWT tokens" + example: "https://accounts.google.com" + - name: audience + type: string + required: true + description: "The expected audience of the JWT tokens" + example: "my-app" diff --git a/middleware/http/oauth2/metadata.yaml b/middleware/http/oauth2/metadata.yaml new file mode 100644 index 0000000000..b6aff861be --- /dev/null +++ b/middleware/http/oauth2/metadata.yaml @@ -0,0 +1,66 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: middleware +name: oauth2 +version: v1 +status: alpha +title: "OAuth2 Authentication" +description: | + The OAuth2 middleware provides OAuth2 authentication for HTTP requests. + It handles OAuth2 flows and token validation for securing API endpoints. +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-oauth2/ +authenticationProfiles: + - title: "OAuth2 Authentication" + description: "Configure OAuth2 authentication with any OAuth2 provider" + metadata: + - name: clientID + type: string + required: true + description: "The OAuth2 client ID from your OAuth2 provider" + example: "your-client-id" + - name: clientSecret + type: string + required: true + description: "The OAuth2 client secret from your OAuth2 provider" + sensitive: true + example: "your-client-secret" + - name: authURL + type: string + required: true + description: "The OAuth2 authorization URL from your provider" + example: "https://accounts.google.com/o/oauth2/v2/auth" + - name: tokenURL + type: string + required: true + description: "The OAuth2 token URL from your provider" + example: "https://oauth2.googleapis.com/token" + - name: scopes + type: string + required: false + description: "OAuth2 scopes to request from your provider" + example: "openid profile email" +metadata: + - name: redirectURL + type: string + required: false + description: "The OAuth2 redirect URL for your application" + example: "http://localhost:8080/callback" + - name: authHeaderName + type: string + required: false + description: "The name of the authorization header to use" + example: "Authorization" + default: "Authorization" + - name: forceHTTPS + type: string + required: false + description: "Whether to force HTTPS for the redirect URL" + example: "true" + default: "false" + - name: pathFilter + type: string + required: false + description: "Regular expression to filter which paths require authentication" + example: "^/api/.*" diff --git a/middleware/http/oauth2clientcredentials/metadata.yaml b/middleware/http/oauth2clientcredentials/metadata.yaml new file mode 100644 index 0000000000..f6db0f192e --- /dev/null +++ b/middleware/http/oauth2clientcredentials/metadata.yaml @@ -0,0 +1,64 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: middleware +name: oauth2clientcredentials +version: v1 +status: alpha +title: "OAuth2 Client Credentials" +description: | + The OAuth2 Client Credentials middleware provides OAuth2 client credentials flow authentication. + It handles machine-to-machine authentication using client credentials. +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-middleware/oauth2clientcredentials/ +authenticationProfiles: + - title: "OAuth2 Client Credentials" + description: "Configure OAuth2 client credentials authentication with any OAuth2 provider" + metadata: + - name: clientID + type: string + required: true + description: "The client ID of your application that is created as part of a credential hosted by a OAuth-enabled platform" + example: "your-client-id" + - name: clientSecret + type: string + required: true + description: "The client secret of your application that is created as part of a credential hosted by a OAuth-enabled platform" + sensitive: true + example: "your-client-secret" + - name: scopes + type: string + required: false + description: "A list of space-delimited, case-sensitive strings of scopes which are typically used for authorization in the application" + example: "https://www.googleapis.com/auth/userinfo.email" + - name: tokenURL + type: string + required: true + description: "The endpoint is used by the client to obtain an access token by presenting its authorization grant or refresh token" + example: "https://accounts.google.com/o/oauth2/token" +metadata: + - name: pathFilter + type: string + required: false + description: "Regular expression to filter which paths require authentication" + example: "^/api/.*" + - name: headerName + type: string + required: true + description: "The authorization header name to forward to your application" + example: "authorization" + - name: endpointParamsQuery + type: string + required: false + description: "Specifies additional parameters for requests to the token endpoint" + example: "param1=value1¶m2=value2" + - name: authStyle + type: integer + required: false + description: "Optionally specifies how the endpoint wants the client ID & client secret sent. 0: Auto-detect (tries both ways and caches the successful way), 1: Sends client_id and client_secret in POST body as application/x-www-form-urlencoded parameters, 2: Sends client_id and client_secret using HTTP Basic Authorization" + example: 0 + default: 0 + allowedValues: + - 0 + - 1 + - 2 \ No newline at end of file diff --git a/middleware/http/opa/metadata.yaml b/middleware/http/opa/metadata.yaml new file mode 100644 index 0000000000..60615e097d --- /dev/null +++ b/middleware/http/opa/metadata.yaml @@ -0,0 +1,65 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: middleware +name: opa +version: v1 +status: alpha +title: "Open Policy Agent (OPA)" +description: | + The OPA middleware allows you to enforce policies on HTTP requests using Open Policy Agent (OPA) Rego policies. + It evaluates incoming requests against your Rego policies and can allow, deny, or modify requests based on the policy results. +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-opa/ +metadata: + - name: rego + type: string + required: true + description: "The Rego policy code that will be evaluated for each request. The policy package must be http and the policy must set data.http.allow" + example: | + package http + + default allow = true + + # Allow may also be an object and include other properties + + # For example, if you wanted to redirect on a policy failure, you could set the status code to 301 and set the location header on the response: + allow = { + "status_code": 301, + "additional_headers": { + "location": "https://my.site/authorize" + } + } { + not jwt.payload["my-claim"] + } + + # You can also allow the request and add additional headers to it: + allow = { + "allow": true, + "additional_headers": { + "x-my-claim": my_claim + } + } { + my_claim := jwt.payload["my-claim"] + } + jwt = { "payload": payload } { + auth_header := input.request.headers["Authorization"] + [_, jwt] := split(auth_header, " ") + [_, payload, _] := io.jwt.decode(jwt) + } + - name: defaultStatus + type: integer + required: false + description: "The status code to return for denied responses" + example: 403 + default: 403 + - name: includedHeaders + type: string + required: false + description: "Comma-separated set of case-insensitive headers to include in the request input. Request headers are not passed to the policy by default. Include to receive incoming request headers in the input" + example: "x-my-custom-header, x-jwt-header" + - name: readBody + type: string + required: false + description: "Controls whether the middleware reads the entire request body in-memory and make it available for policy decisions" + example: "false" diff --git a/middleware/http/ratelimit/metadata.yaml b/middleware/http/ratelimit/metadata.yaml new file mode 100644 index 0000000000..c43706b4c2 --- /dev/null +++ b/middleware/http/ratelimit/metadata.yaml @@ -0,0 +1,19 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: middleware +name: ratelimit +version: v1 +status: stable +title: "Rate Limiting" +description: | + The Rate Limiting middleware provides request rate limiting functionality. + It can limit requests based on various criteria like IP address, user, or custom keys. +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-rate-limit/ +metadata: + - name: maxRequestsPerSecond + type: integer + required: true + description: "Maximum number of requests allowed per second" + example: 100 \ No newline at end of file diff --git a/middleware/http/routerchecker/metadata.yaml b/middleware/http/routerchecker/metadata.yaml new file mode 100644 index 0000000000..d7ba54ff43 --- /dev/null +++ b/middleware/http/routerchecker/metadata.yaml @@ -0,0 +1,18 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: middleware +name: routerchecker +version: v1 +status: alpha +title: "Router Checker" +description: | + The RouterChecker HTTP middleware component leverages regexp to check the validity of HTTP request routing to prevent invalid routers from entering the Dapr cluster. In turn, the RouterChecker component filters out bad requests and reduces noise in the telemetry and log data. +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-routerchecker/ +metadata: + - name: rule + type: string + required: true + description: "Regular expression to filter which paths are allowed" + example: "^[A-Za-z0-9/._-]+$" diff --git a/middleware/http/sentinel/metadata.yaml b/middleware/http/sentinel/metadata.yaml new file mode 100644 index 0000000000..c106454f45 --- /dev/null +++ b/middleware/http/sentinel/metadata.yaml @@ -0,0 +1,90 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: middleware +name: sentinel +version: v1 +status: alpha +title: "Sentinel" +description: | + Use Sentinel middleware to guarantee the reliability and resiliency of your application. + Sentinel is a powerful fault-tolerance component that takes "flow" as the breakthrough point and covers multiple fields including flow control, traffic shaping, concurrency limiting, circuit breaking, and adaptive system protection to guarantee the reliability and resiliency of microservices. + + The Sentinel HTTP middleware enables Dapr to facilitate Sentinel's powerful abilities to protect your application. You can refer to Sentinel Wiki for more details on Sentinel. +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-sentinel/ +metadata: + - name: appName + type: string + required: true + description: "The application name for Sentinel" + example: "nodeapp" + - name: logDir + type: string + required: false + description: "Directory for Sentinel log files" + example: "/var/tmp" + - name: flowRules + type: string + required: false + description: "JSON array of flow control rules to limit request rate" + example: | + [ + { + "resource": "POST:/v1.0/invoke/nodeapp/method/neworder", + "threshold": 10, + "tokenCalculateStrategy": 0, + "controlBehavior": 0 + } + ] + - name: circuitBreakerRules + type: string + required: false + description: "JSON array of circuit breaker rules to handle failures" + example: | + [ + { + "resource": "POST:/v1.0/invoke/nodeapp/method/neworder", + "minRequestAmount": 5, + "statIntervalMs": 1000, + "maxAllowedRtMs": 50, + "maxSlowRequestRatio": 0.5 + } + ] + - name: hotSpotParamRules + type: string + required: false + description: "JSON array of hotspot parameter rules for parameter-based flow control" + example: | + [ + { + "resource": "POST:/v1.0/invoke/nodeapp/method/neworder", + "paramIdx": 0, + "threshold": 10, + "maxQueueingTimeMs": 500 + } + ] + - name: isolationRules + type: string + required: false + description: "JSON array of isolation rules for thread pool isolation" + example: | + [ + { + "resource": "POST:/v1.0/invoke/nodeapp/method/neworder", + "maxConcurrency": 10, + "maxQueueingTimeMs": 500 + } + ] + - name: systemRules + type: string + required: false + description: "JSON array of system protection rules for overall system protection" + example: | + [ + { + "avgRt": 50, + "maxThread": 10, + "qps": 20 + } + ] diff --git a/middleware/http/wasm/metadata.yaml b/middleware/http/wasm/metadata.yaml new file mode 100644 index 0000000000..26ae9fb586 --- /dev/null +++ b/middleware/http/wasm/metadata.yaml @@ -0,0 +1,27 @@ +# yaml-language-server: $schema=../../../component-metadata-schema.json +schemaVersion: v1 +type: middleware +name: wasm +version: v1 +status: alpha +title: "WebAssembly (WASM)" +description: | + The WebAssembly middleware allows you to run custom logic written in WASM. + It can execute WASM modules to process HTTP requests and responses. +urls: + - title: Reference + url: https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-wasm/ +metadata: + - name: url + type: string + required: true + description: "URL of the WASM module" + example: "https://example.com/middleware.wasm" + - name: guestConfig + required: false + description: "Configuration object passed to the WASM module" + example: | + { + "timeout": "5s", + "maxMemory": "10MB" + } diff --git a/nameresolution/sqlite/metadata.yaml b/nameresolution/sqlite/metadata.yaml index 8ebdf61c4a..0d59bec957 100644 --- a/nameresolution/sqlite/metadata.yaml +++ b/nameresolution/sqlite/metadata.yaml @@ -3,7 +3,7 @@ schemaVersion: v1 type: nameresolution name: sqlite version: v1 -status: stable +status: alpha title: "SQLite" urls: - title: Reference From 4c9cfdb39707553492c7ad6f19f577d4a7d5bf27 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Thu, 7 Aug 2025 15:08:36 -0500 Subject: [PATCH 09/32] style: clean up sam logs Signed-off-by: Samantha Coyle --- .../cmd/cmd-check-component-registrations.go | 101 +++++++++++------- 1 file changed, 60 insertions(+), 41 deletions(-) diff --git a/.build-tools/cmd/cmd-check-component-registrations.go b/.build-tools/cmd/cmd-check-component-registrations.go index 8bbc88841a..5dac069d2d 100644 --- a/.build-tools/cmd/cmd-check-component-registrations.go +++ b/.build-tools/cmd/cmd-check-component-registrations.go @@ -22,6 +22,8 @@ import ( "github.com/spf13/cobra" ) +var verbose bool + // checkComponentRegistrationsCmd represents the check-component-registrations command // go run . check-component-registrations // This automates an endgame task that must be completed before a release. @@ -62,6 +64,7 @@ This is a required step before an official Dapr release.`, } func init() { + checkComponentRegistrationsCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose output") rootCmd.AddCommand(checkComponentRegistrationsCmd) } @@ -75,7 +78,8 @@ func checkStateComponents() { // Yugabyte are supported via the postgres component, so in contrib this is covered by the postgresql component in the contrib list. // Also, postgres = postgresql, so we can ignore postgres. - ignoreDaprComponents := []string{"yugabyte", "yugabytedb", "postgres"} + // also sqlite3 is an alias for sqlite so we can ignore one. + ignoreDaprComponents := []string{"yugabyte", "yugabytedb", "postgres", "sqlite3"} ignoreContribComponents := []string{"azure.blobstorage.internal"} checkComponents("state", ignoreDaprComponents, ignoreContribComponents) } @@ -83,9 +87,11 @@ func checkStateComponents() { func checkPubSubComponents() { fmt.Println("\nChecking pubsub components...") - // mqtt3 = mqtt, so ignore mqtt3 for now in this cli - ignoreDaprComponents := []string{"mqtt3"} - ignoreContribComponents := []string{"mqtt3"} + // mqtt3 = mqtt, so ignore mqtt (keep mqtt3 since it exists in contrib) + // azure.servicebusqueues is an alias for azure.servicebus.queues (keep azure.servicebus.queues) so ignore it + // azure.servicebus is an alias for azure.servicebus.topics (keep azure.servicebus.topics) so ignore it + ignoreDaprComponents := []string{"mqtt", "azure.servicebusqueues", "azure.servicebus"} + ignoreContribComponents := []string{} checkComponents("pubsub", ignoreDaprComponents, ignoreContribComponents) } @@ -149,8 +155,10 @@ func checkComponents(componentType string, ignoreDaprComponents []string, ignore return } - fmt.Printf("Components to ignore: %v\n", ignoreDaprComponents) - fmt.Printf("Dapr components before filtering: %v\n", daprComponents) + if verbose { + fmt.Printf("Components to ignore: %v\n", ignoreDaprComponents) + fmt.Printf("Dapr components before filtering: %v\n", daprComponents) + } // Filter out components to ignore filteredDaprComponents := make([]string, 0, len(daprComponents)) @@ -158,7 +166,9 @@ func checkComponents(componentType string, ignoreDaprComponents []string, ignore if !slices.Contains(ignoreDaprComponents, comp) { filteredDaprComponents = append(filteredDaprComponents, comp) } else { - fmt.Printf("Ignoring component: %s\n", comp) + if verbose { + fmt.Printf("Ignoring component: %s\n", comp) + } } } daprComponents = filteredDaprComponents @@ -168,7 +178,9 @@ func checkComponents(componentType string, ignoreDaprComponents []string, ignore if !slices.Contains(ignoreContribComponents, comp) { filteredContribComponents = append(filteredContribComponents, comp) } else { - fmt.Printf("Ignoring component: %s\n", comp) + if verbose { + fmt.Printf("Ignoring component: %s\n", comp) + } } } contribComponents = filteredContribComponents @@ -329,15 +341,12 @@ func validateComponents(contribComponents []string, componentType string) ([]str metadataErr := checkMetadataFile(contrib, componentType) if registrationErr != nil { - fmt.Printf("sam the registration err: %v\n", registrationErr) missingRegistrations = append(missingRegistrations, contrib) } if buildTagErr != nil { - fmt.Printf("sam the build tag err: %v\n", buildTagErr) missingBuildTags = append(missingBuildTags, contrib) } if metadataErr != nil { - fmt.Printf("sam the metadata err: %v\n", metadataErr) missingMetadata = append(missingMetadata, contrib) } } @@ -356,11 +365,31 @@ func checkComponentRegistration(contrib, componentType string) error { return fmt.Errorf("registration file for '%s' not found: %s", contrib, compFileName) } + // Check if this is a versioned component + // EX: postgresql.v1, azure.blobstorage.v2 + parts := strings.Split(contrib, ".") + if len(parts) >= 2 { + lastPart := parts[len(parts)-1] + if strings.HasPrefix(lastPart, "v") && len(lastPart) <= 3 { + // This is a versioned component, check if the base component is registered + baseComponent := strings.Join(parts[:len(parts)-1], ".") + if err := checkComponentIsActuallyRegisteredInFile(baseComponent, compFileName); err != nil { + return fmt.Errorf("versioned component '%s' base component '%s' not registered in %s: %v", contrib, baseComponent, compFileName, err) + } + if verbose { + fmt.Printf("Versioned component '%s' properly registered via base component '%s' in %s\n", contrib, baseComponent, compFileName) + } + return nil + } + } + if err := checkComponentIsActuallyRegisteredInFile(contrib, compFileName); err != nil { return fmt.Errorf("component '%s' not registered in %s: %v", contrib, compFileName, err) } - fmt.Printf("Component '%s' properly registered in %s\n", contrib, compFileName) + if verbose { + fmt.Printf("Component '%s' properly registered in %s\n", contrib, compFileName) + } return nil } @@ -394,7 +423,6 @@ func checkBuildTag(contrib, componentType string) error { // EX: postgresql.v1 -> postgresql // EX: azure.blobstorage.v1 -> blobstorage func normalizeComponentName(contrib string) string { - // fmt.Printf("sam the contrib: %s\n", contrib) if !strings.Contains(contrib, ".") { return contrib } @@ -407,15 +435,12 @@ func normalizeComponentName(contrib string) string { // Handle 2-part names (vendor.component) if len(parts) == 2 { - // fmt.Printf("sam the parts: %v\n", parts) // Strip vendor prefixes if slices.Contains(vendorPrefixes, parts[0]) { - // fmt.Printf("sam the parts[1]: %s\n", parts[1]) return parts[1] } // Strip version suffixes if slices.Contains(versionSuffixes, parts[1]) { - // fmt.Printf("sam the parts[0]: %s\n", parts[0]) return parts[0] } } @@ -439,8 +464,6 @@ func normalizeComponentName(contrib string) string { } } - fmt.Printf("sam the contrib normalized: %s\n", contrib) - return contrib } @@ -468,7 +491,9 @@ func checkMetadataFile(contrib, componentType string) error { return fmt.Errorf("metadata file for '%s' not found: %s", contrib, metadataFile) } - fmt.Printf("Metadata file for '%s' found: %s\n", contrib, metadataFile) + if verbose { + fmt.Printf("Metadata file for '%s' found: %s\n", contrib, metadataFile) + } return nil } @@ -621,22 +646,20 @@ func parseRegisteredComponents(output string, componentType string) []string { return r == '\n' }) - // Group lines by file to handle multi-line registrations - fileGroups := make(map[string]bool) + // Extract unique file paths from grep output + uniqueFiles := make(map[string]bool) for _, line := range lines { - // Extract file path from the line (format: filepath:content) colonIndex := strings.Index(line, ":") if colonIndex == -1 { continue } filePath := line[:colonIndex] - fileGroups[filePath] = true + uniqueFiles[filePath] = true } - // Process each file's content - for filePath := range fileGroups { - // skip the uppercase component as it has a ton of magic strings that we must ignore. - // all other components only have the component registration string in this file so it is an outlier. + // Process each registration file + for filePath := range uniqueFiles { + // Skip uppercase component as it contains many magic strings that aren't component names if strings.Contains(filePath, "uppercase") { continue } @@ -645,7 +668,6 @@ func parseRegisteredComponents(output string, componentType string) []string { cmd := exec.Command("cat", filePath) fileContent, err := cmd.Output() if err != nil { - fmt.Printf("sam the err: %v\n", err) continue } @@ -653,31 +675,27 @@ func parseRegisteredComponents(output string, componentType string) []string { return r == '\n' }) - // Check if this file contains registration calls - componentRegistration := false + // Check if this file contains registration calls and find the starting line + hasRegistrationCalls := false lineToContinueFrom := 0 for i, line := range fileLines { if componentType == "bindings" { - if strings.Contains(line, "RegisterInputBinding") { - componentRegistration = true - lineToContinueFrom = i - break - } else if strings.Contains(line, "RegisterOutputBinding") { - componentRegistration = true + if strings.Contains(line, "RegisterInputBinding") || strings.Contains(line, "RegisterOutputBinding") { + hasRegistrationCalls = true lineToContinueFrom = i break } } else { if strings.Contains(line, "RegisterComponent") { - componentRegistration = true + hasRegistrationCalls = true lineToContinueFrom = i break } } } - // If file has registration calls, extract all quoted strings from the entire file - if componentRegistration { + // Extract component names from lines starting from the first registration call + if hasRegistrationCalls { for i, line := range fileLines { if i < lineToContinueFrom { continue @@ -711,11 +729,12 @@ func extractAllQuotedStrings(line string) []string { } quotedString := rest[:endQuoteIndex] - quotedStrings = append(quotedStrings, quotedString) + if quotedString != "v1" && quotedString != "v2" { + quotedStrings = append(quotedStrings, quotedString) + } start = quoteIndex + 1 + endQuoteIndex + 1 } - fmt.Printf("sam the quotedStrings: %v\n", quotedStrings) return quotedStrings } From 10e317405fa126d65112af4e1d8329240b897ea0 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Thu, 7 Aug 2025 15:24:13 -0500 Subject: [PATCH 10/32] style: add allllll other component types Signed-off-by: Samantha Coyle --- .build-tools/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.build-tools/README.md b/.build-tools/README.md index ea6c484357..cbc014b75b 100644 --- a/.build-tools/README.md +++ b/.build-tools/README.md @@ -34,5 +34,5 @@ go run . check-component-registrations ./build-tools check-component-registrations ``` -This command will scan all component types (conversation, state, secretstores, pubsub, bindings, configuration) and report any missing registrations. +This command will scan all component types (conversation, state, secretstores, pubsub, bindings, configuration, nameresolution, middleware, cryptography, lock) and report any missing registrations. This is part of the release endgame tasking to ensure all components properly register within runtime as expected. From 556437c2bc41c3465765dd0f8ed39c01d327b686 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Thu, 7 Aug 2025 16:26:43 -0500 Subject: [PATCH 11/32] fix: make build happy Signed-off-by: Samantha Coyle --- bindings/azure/servicebusqueues/servicebusqueues.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/azure/servicebusqueues/servicebusqueues.go b/bindings/azure/servicebusqueues/servicebusqueues.go index af2bc3b534..bc1291ac32 100644 --- a/bindings/azure/servicebusqueues/servicebusqueues.go +++ b/bindings/azure/servicebusqueues/servicebusqueues.go @@ -62,7 +62,7 @@ func (a *AzureServiceBusQueues) Init(ctx context.Context, metadata bindings.Meta return err } - a.client, err = impl.NewClient(a.metadata, metadata.Properties) + a.client, err = impl.NewClient(a.metadata, metadata.Properties, a.logger) if err != nil { return err } From cea8dd22b22a0d1dc769efe92b5de752353ddd66 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Thu, 7 Aug 2025 16:55:19 -0500 Subject: [PATCH 12/32] fix(build): update to add newline i removed on accident Signed-off-by: Samantha Coyle --- bindings/commercetools/metadata.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bindings/commercetools/metadata.yaml b/bindings/commercetools/metadata.yaml index e92118bfba..c664efcd1c 100644 --- a/bindings/commercetools/metadata.yaml +++ b/bindings/commercetools/metadata.yaml @@ -37,7 +37,8 @@ metadata: required: true description: "The Commercetools region" example: '"gcp-europe-west1"' - default: '"gcp-europe-west1"' - name: provider + default: '"gcp-europe-west1"' + - name: provider required: true description: "The Commercetools provider" example: '"gcp"' From 213283238e8b98354ad972a5dd15e711aa2c6aff Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Thu, 7 Aug 2025 17:11:54 -0500 Subject: [PATCH 13/32] style: ensure we use proper types in metadata files Signed-off-by: Samantha Coyle --- bindings/apns/metadata.yaml | 2 +- bindings/kubemq/metadata.yaml | 4 +- bindings/rethinkdb/statechange/metadata.yaml | 2 +- .../oauth2clientcredentials/metadata.yaml | 2 +- middleware/http/opa/metadata.yaml | 33 +--------------- middleware/http/opa/middleware.go | 1 + middleware/http/ratelimit/metadata.yaml | 2 +- .../hashicorp/consul/configuration.go | 4 +- nameresolution/hashicorp/consul/metadata.yaml | 38 ++++--------------- pubsub/jetstream/metadata.yaml | 18 ++++----- pubsub/rocketmq/metadata.yaml | 28 +++++++------- state/couchbase/metadata.yaml | 4 +- state/etcd/metadata.yaml | 2 +- state/rethinkdb/metadata.yaml | 2 +- state/zookeeper/metadata.yaml | 4 +- 15 files changed, 47 insertions(+), 99 deletions(-) diff --git a/bindings/apns/metadata.yaml b/bindings/apns/metadata.yaml index 960d1f1971..08b4b8a7f9 100644 --- a/bindings/apns/metadata.yaml +++ b/bindings/apns/metadata.yaml @@ -34,7 +34,7 @@ authenticationProfiles: example: '"-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg..."' metadata: - name: development - type: boolean + type: bool required: false description: "The APNS environment is development or not" example: true diff --git a/bindings/kubemq/metadata.yaml b/bindings/kubemq/metadata.yaml index 994d7d4675..b5b70d7605 100644 --- a/bindings/kubemq/metadata.yaml +++ b/bindings/kubemq/metadata.yaml @@ -37,13 +37,13 @@ metadata: description: The KubeMQ channel name. example: "my-channel" - name: pollMaxItems - type: int + type: number required: false description: The maximum number of items to poll. example: 10 default: 1 - name: pollTimeoutSeconds - type: int + type: number required: false description: The timeout in seconds for polling. example: 3600 diff --git a/bindings/rethinkdb/statechange/metadata.yaml b/bindings/rethinkdb/statechange/metadata.yaml index 4e63d8f1cb..c782658385 100644 --- a/bindings/rethinkdb/statechange/metadata.yaml +++ b/bindings/rethinkdb/statechange/metadata.yaml @@ -69,7 +69,7 @@ metadata: example: false default: false - name: numRetries - type: int + type: number required: false description: Number of times to retry queries on connection errors. example: 3 diff --git a/middleware/http/oauth2clientcredentials/metadata.yaml b/middleware/http/oauth2clientcredentials/metadata.yaml index f6db0f192e..906ca7836b 100644 --- a/middleware/http/oauth2clientcredentials/metadata.yaml +++ b/middleware/http/oauth2clientcredentials/metadata.yaml @@ -53,7 +53,7 @@ metadata: description: "Specifies additional parameters for requests to the token endpoint" example: "param1=value1¶m2=value2" - name: authStyle - type: integer + type: number required: false description: "Optionally specifies how the endpoint wants the client ID & client secret sent. 0: Auto-detect (tries both ways and caches the successful way), 1: Sends client_id and client_secret in POST body as application/x-www-form-urlencoded parameters, 2: Sends client_id and client_secret using HTTP Basic Authorization" example: 0 diff --git a/middleware/http/opa/metadata.yaml b/middleware/http/opa/metadata.yaml index 60615e097d..84c93b126b 100644 --- a/middleware/http/opa/metadata.yaml +++ b/middleware/http/opa/metadata.yaml @@ -16,39 +16,8 @@ metadata: type: string required: true description: "The Rego policy code that will be evaluated for each request. The policy package must be http and the policy must set data.http.allow" - example: | - package http - - default allow = true - - # Allow may also be an object and include other properties - - # For example, if you wanted to redirect on a policy failure, you could set the status code to 301 and set the location header on the response: - allow = { - "status_code": 301, - "additional_headers": { - "location": "https://my.site/authorize" - } - } { - not jwt.payload["my-claim"] - } - - # You can also allow the request and add additional headers to it: - allow = { - "allow": true, - "additional_headers": { - "x-my-claim": my_claim - } - } { - my_claim := jwt.payload["my-claim"] - } - jwt = { "payload": payload } { - auth_header := input.request.headers["Authorization"] - [_, jwt] := split(auth_header, " ") - [_, payload, _] := io.jwt.decode(jwt) - } - name: defaultStatus - type: integer + type: number required: false description: "The status code to return for denied responses" example: 403 diff --git a/middleware/http/opa/middleware.go b/middleware/http/opa/middleware.go index 5de765da68..f2eecbbff7 100644 --- a/middleware/http/opa/middleware.go +++ b/middleware/http/opa/middleware.go @@ -42,6 +42,7 @@ import ( type Status int type middlewareMetadata struct { + // TODO: rego field needs to be updated to support the objects as input that the docs show for this field as inputs Rego string `json:"rego" mapstructure:"rego"` DefaultStatus Status `json:"defaultStatus,omitempty" mapstructure:"defaultStatus"` IncludedHeaders string `json:"includedHeaders,omitempty" mapstructure:"includedHeaders"` diff --git a/middleware/http/ratelimit/metadata.yaml b/middleware/http/ratelimit/metadata.yaml index c43706b4c2..fd29d14855 100644 --- a/middleware/http/ratelimit/metadata.yaml +++ b/middleware/http/ratelimit/metadata.yaml @@ -13,7 +13,7 @@ urls: url: https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-rate-limit/ metadata: - name: maxRequestsPerSecond - type: integer + type: number required: true description: "Maximum number of requests allowed per second" example: 100 \ No newline at end of file diff --git a/nameresolution/hashicorp/consul/configuration.go b/nameresolution/hashicorp/consul/configuration.go index 15e66ca756..017894c114 100644 --- a/nameresolution/hashicorp/consul/configuration.go +++ b/nameresolution/hashicorp/consul/configuration.go @@ -30,7 +30,7 @@ const defaultDaprPortMetaKey string = "DAPR_PORT" // default key for DaprPort in // that way breaking changes in future versions of the consul api cannot break user configuration. type intermediateConfig struct { Client *Config - Checks []*AgentServiceCheck + Checks []*AgentServiceCheck // TODO: update this to bring properly to metadata Tags []string Meta map[string]string QueryOptions *QueryOptions @@ -43,7 +43,7 @@ type intermediateConfig struct { type configSpec struct { Client *consul.Config - Checks []*consul.AgentServiceCheck + Checks []*consul.AgentServiceCheck // TODO: update this to bring properly to metadata Tags []string Meta map[string]string QueryOptions *consul.QueryOptions diff --git a/nameresolution/hashicorp/consul/metadata.yaml b/nameresolution/hashicorp/consul/metadata.yaml index 70d11ee740..40b58a2451 100644 --- a/nameresolution/hashicorp/consul/metadata.yaml +++ b/nameresolution/hashicorp/consul/metadata.yaml @@ -56,7 +56,7 @@ authenticationProfiles: description: Path to the client private key file. example: "/path/to/client.key" - name: insecureSkipVerify - type: boolean + type: bool required: false description: Skip TLS certificate verification. example: false @@ -95,50 +95,28 @@ metadata: example: "DAPR_PORT" default: "DAPR_PORT" - name: selfRegister - type: boolean + type: bool required: false description: Whether to register this service with Consul. example: true default: false - name: selfDeregister - type: boolean + type: bool required: false description: Whether to deregister this service from Consul on shutdown. example: true default: false - name: useCache - type: boolean + type: bool required: false description: Whether to use caching for service lookups. example: true default: false - name: tags - type: array + type: string required: false description: Tags to associate with the service registration. - example: ["dapr", "v1"] - - name: meta - type: object - required: false - description: Metadata to associate with the service registration. - example: | - { - "version": "v1", - "environment": "production" - } - - name: checks - type: array - required: false - description: Health checks to associate with the service registration. - example: | - [ - { - "name": "dapr-health", - "http": "http://localhost:3500/health", - "interval": "10s", - "timeout": "5s" - } - ] + example: "dapr,v1" - name: namespace type: string required: false @@ -166,13 +144,13 @@ metadata: description: The wait time for Consul queries. example: "5s" - name: allowStale - type: boolean + type: bool required: false description: Whether to allow stale results in queries. example: true default: false - name: requireConsistent - type: boolean + type: bool required: false description: Whether to require consistent reads in queries. example: false diff --git a/pubsub/jetstream/metadata.yaml b/pubsub/jetstream/metadata.yaml index 22426afcc2..9074ebb732 100644 --- a/pubsub/jetstream/metadata.yaml +++ b/pubsub/jetstream/metadata.yaml @@ -95,12 +95,12 @@ metadata: description: The queue group name for load balancing. example: "my-queue-group" - name: startSequence - type: integer + type: number required: false description: The starting sequence number for message delivery. example: 1 - name: startTime - type: integer + type: number required: false description: The starting time (Unix timestamp) for message delivery. example: 1640995200 @@ -117,17 +117,17 @@ metadata: description: The acknowledgment wait time. example: "30s" - name: maxDeliver - type: integer + type: number required: false description: The maximum number of message deliveries. example: 5 - name: maxAckPending - type: integer + type: number required: false description: The maximum number of unacknowledged messages. example: 100 - name: replicas - type: integer + type: number required: false description: The number of stream replicas. example: 3 @@ -138,7 +138,7 @@ metadata: example: false default: false - name: rateLimit - type: integer + type: number required: false description: The rate limit for message consumption. example: 1000 @@ -185,12 +185,12 @@ metadata: example: "single" default: "single" - name: backOff - type: array + type: string required: false description: The backoff configuration for message delivery for the consumer. - example: "[1s, 2s, 4s]" + example: "1s" - name: maxAckPending - type: integer + type: number required: false description: The maximum number of unacknowledged messages for the consumer. example: 100 \ No newline at end of file diff --git a/pubsub/rocketmq/metadata.yaml b/pubsub/rocketmq/metadata.yaml index f58b49b8e9..d844b50cd4 100644 --- a/pubsub/rocketmq/metadata.yaml +++ b/pubsub/rocketmq/metadata.yaml @@ -59,7 +59,7 @@ metadata: description: The RocketMQ name server domain. example: "rocketmq.example.com" - name: retries - type: integer + type: number required: false description: The number of retry attempts for sending messages. example: 3 @@ -102,17 +102,17 @@ metadata: example: false default: false - name: consumeMessageBatchMaxSize - type: integer + type: number required: false description: The maximum batch size for message consumption. example: 10 - name: consumeConcurrentlyMaxSpan - type: integer + type: number required: false description: The maximum span for concurrent consumption. example: 10 - name: maxReconsumeTimes - type: integer + type: number required: false description: The maximum number of reconsume times. -1 means 16 times. example: 10000 @@ -123,49 +123,49 @@ metadata: example: true default: false - name: consumeTimeout - type: integer + type: number required: false description: The consume timeout in minutes. example: 10 - name: consumerPullTimeout - type: integer + type: number required: false description: The consumer pull timeout in milliseconds. example: 30 default: 30 - name: pullInterval - type: integer + type: number required: false description: The pull interval in minutes. example: 100 default: 100 - name: consumerBatchSize - type: integer + type: number required: false description: The consumer batch size. example: 10 - name: pullBatchSize - type: integer + type: number required: false description: The pull batch size. example: 10 - name: pullThresholdForQueue - type: integer + type: number required: false description: The pull threshold for queue. example: 100 - name: pullThresholdForTopic - type: integer + type: number required: false description: The pull threshold for topic. example: 100 - name: pullThresholdSizeForQueue - type: integer + type: number required: false description: The pull threshold size for queue. example: 10 - name: pullThresholdSizeForTopic - type: integer + type: number required: false description: The pull threshold size for topic. example: 10 @@ -175,7 +175,7 @@ metadata: description: The content type for messages. example: "json" - name: sendTimeOutSec - type: integer + type: number required: false description: The send timeout in seconds. example: 10 diff --git a/state/couchbase/metadata.yaml b/state/couchbase/metadata.yaml index 6b30d80448..e1269e2ed3 100644 --- a/state/couchbase/metadata.yaml +++ b/state/couchbase/metadata.yaml @@ -30,13 +30,13 @@ authenticationProfiles: example: "default" metadata: - name: numReplicasDurableReplication - type: integer + type: number required: false description: The number of replicas for durable replication. example: 1 default: 0 - name: numReplicasDurablePersistence - type: integer + type: number required: false description: The number of replicas for durable persistence. example: 1 diff --git a/state/etcd/metadata.yaml b/state/etcd/metadata.yaml index ccca945337..e357b8785a 100644 --- a/state/etcd/metadata.yaml +++ b/state/etcd/metadata.yaml @@ -62,7 +62,7 @@ metadata: example: "my_key_prefix_path" default: "" - name: maxTxnOps - type: integer + type: number required: false description: Maximum number of operations allowed in a transaction. example: 128 diff --git a/state/rethinkdb/metadata.yaml b/state/rethinkdb/metadata.yaml index a56d3e9b5c..badc23f6d9 100644 --- a/state/rethinkdb/metadata.yaml +++ b/state/rethinkdb/metadata.yaml @@ -63,7 +63,7 @@ metadata: example: false default: false - name: numRetries - type: int + type: number required: false description: Number of times to retry queries on connection errors. example: 3 diff --git a/state/zookeeper/metadata.yaml b/state/zookeeper/metadata.yaml index aea852d71e..10bc49bff7 100644 --- a/state/zookeeper/metadata.yaml +++ b/state/zookeeper/metadata.yaml @@ -20,13 +20,13 @@ metadata: description: Session timeout in seconds. example: "10s" - name: maxBufferSize - type: integer + type: number required: false description: The maximum buffer size in bytes. example: 1048576 default: 1048576 # 1MB - name: maxConnBufferSize - type: integer + type: number required: false description: The maximum connection buffer size in bytes. example: 1048576 From 305a1d395853c691489546d674ddf8e552a31f83 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Fri, 8 Aug 2025 08:57:25 -0500 Subject: [PATCH 14/32] style: update metadata files for the analyzer to be happy Signed-off-by: Samantha Coyle --- .build-tools/pkg/metadataanalyzer/utils.go | 3 +- bindings/alicloud/oss/metadata.yaml | 5 ++ bindings/alicloud/tablestore/metadata.yaml | 5 ++ bindings/aws/dynamodb/metadata.yaml | 6 ++- bindings/aws/kinesis/metadata.yaml | 6 ++- bindings/aws/sqs/metadata.yaml | 6 ++- bindings/cloudflare/queues/metadata.yaml | 6 ++- bindings/kubernetes/metadata.yaml | 2 +- bindings/rethinkdb/statechange/metadata.yaml | 53 ++++++++++++++++++- bindings/rethinkdb/statechange/statechange.go | 4 +- bindings/twilio/sendgrid/metadata.yaml | 10 +++- lock/redis/metadata.yaml | 31 +++++++++++ pubsub/rocketmq/metadata.yaml | 12 ++++- state/gcp/firestore/metadata.yaml | 18 ++++++- state/hazelcast/hazelcast.go | 4 +- state/hazelcast/metadata.yaml | 4 +- state/oci/objectstorage/metadata.yaml | 7 ++- state/oci/objectstorage/objectstorage.go | 10 ++-- state/rethinkdb/metadata.yaml | 53 ++++++++++++++++++- 19 files changed, 221 insertions(+), 24 deletions(-) diff --git a/.build-tools/pkg/metadataanalyzer/utils.go b/.build-tools/pkg/metadataanalyzer/utils.go index 91b26a2bdf..40f8751412 100644 --- a/.build-tools/pkg/metadataanalyzer/utils.go +++ b/.build-tools/pkg/metadataanalyzer/utils.go @@ -120,7 +120,7 @@ func GenerateMetadataAnalyzer(contribRoot string, componentFolders []string, out if methodFinderErr == nil { methodFound = true } - case "crypto": + case "cryptography": method, methodFinderErr = getConstructorMethod("contribCrypto.SubtleCrypto", parsedFile) if methodFinderErr == nil { methodFound = true @@ -136,6 +136,7 @@ func GenerateMetadataAnalyzer(contribRoot string, componentFolders []string, out methodFound = true } } + // TODO: add conversation, nameresolution if methodFound { pkgs[packageName] = PkgInfo{ diff --git a/bindings/alicloud/oss/metadata.yaml b/bindings/alicloud/oss/metadata.yaml index 4831e0a2b2..6746615f68 100644 --- a/bindings/alicloud/oss/metadata.yaml +++ b/bindings/alicloud/oss/metadata.yaml @@ -19,6 +19,11 @@ authenticationProfiles: description: | Authenticate using AliCloud access key credentials. metadata: + - name: accessKey + required: false + description: "The AliCloud access key" + example: '"your-access-key"' + sensitive: true - name: accessKeyID required: false description: "The AliCloud access key ID" diff --git a/bindings/alicloud/tablestore/metadata.yaml b/bindings/alicloud/tablestore/metadata.yaml index b4a7ff1335..80ab4bc051 100644 --- a/bindings/alicloud/tablestore/metadata.yaml +++ b/bindings/alicloud/tablestore/metadata.yaml @@ -19,6 +19,11 @@ authenticationProfiles: description: | Authenticate using AliCloud access key credentials. metadata: + - name: accessKey + required: false + description: "The AliCloud access key" + example: '"your-access-key"' + sensitive: true - name: accessKeyID required: true description: "The AliCloud access key ID" diff --git a/bindings/aws/dynamodb/metadata.yaml b/bindings/aws/dynamodb/metadata.yaml index 9581e72b7c..d2da104054 100644 --- a/bindings/aws/dynamodb/metadata.yaml +++ b/bindings/aws/dynamodb/metadata.yaml @@ -41,4 +41,8 @@ metadata: - name: table required: true description: "The DynamoDB table name" - example: '"my-table"' \ No newline at end of file + example: '"my-table"' + - name: endpoint + required: false + description: "The AWS DynamoDB endpoint" + example: '"https://dynamodb.us-east-1.amazonaws.com"' \ No newline at end of file diff --git a/bindings/aws/kinesis/metadata.yaml b/bindings/aws/kinesis/metadata.yaml index b72e866ee7..719d07a7a5 100644 --- a/bindings/aws/kinesis/metadata.yaml +++ b/bindings/aws/kinesis/metadata.yaml @@ -59,4 +59,8 @@ metadata: - name: partitionKey required: false description: "The partition key for the Kinesis stream" - example: '"my-partition-key"' \ No newline at end of file + example: '"my-partition-key"' + - name: endpoint + required: false + description: "The AWS Kinesis endpoint" + example: '"https://kinesis.us-east-1.amazonaws.com"' \ No newline at end of file diff --git a/bindings/aws/sqs/metadata.yaml b/bindings/aws/sqs/metadata.yaml index 47ca0f43d4..6a05b4a214 100644 --- a/bindings/aws/sqs/metadata.yaml +++ b/bindings/aws/sqs/metadata.yaml @@ -43,4 +43,8 @@ metadata: - name: queueName required: true description: "The SQS queue name" - example: '"my-queue"' \ No newline at end of file + example: '"my-queue"' + - name: endpoint + required: false + description: "The AWS SQS endpoint" + example: '"https://sqs.us-east-1.amazonaws.com"' \ No newline at end of file diff --git a/bindings/cloudflare/queues/metadata.yaml b/bindings/cloudflare/queues/metadata.yaml index ea9eb0077b..4c6c5310d6 100644 --- a/bindings/cloudflare/queues/metadata.yaml +++ b/bindings/cloudflare/queues/metadata.yaml @@ -61,4 +61,8 @@ metadata: required: false description: "Timeout for network requests in seconds" example: '20' - default: '20' \ No newline at end of file + default: '20' + - name: queueName + required: true + description: "The Cloudflare queue name" + example: '"my-queue"' \ No newline at end of file diff --git a/bindings/kubernetes/metadata.yaml b/bindings/kubernetes/metadata.yaml index 06693841b7..afe37a03dc 100644 --- a/bindings/kubernetes/metadata.yaml +++ b/bindings/kubernetes/metadata.yaml @@ -25,7 +25,7 @@ metadata: description: "The path to the kubeconfig file" example: '"~/.kube/config"' default: '"~/.kube/config"' - - name: resyncPeriodInSec + - name: resyncPeriod required: false type: duration description: "The resync period in seconds" diff --git a/bindings/rethinkdb/statechange/metadata.yaml b/bindings/rethinkdb/statechange/metadata.yaml index c782658385..78b24d3e71 100644 --- a/bindings/rethinkdb/statechange/metadata.yaml +++ b/bindings/rethinkdb/statechange/metadata.yaml @@ -84,4 +84,55 @@ metadata: required: false description: Whether to enable opentracing for queries. example: false - default: false \ No newline at end of file + default: false + - name: write_timeout + type: string + required: false + description: Write timeout duration." + example: "10s" + - name: read_timeout + type: string + required: false + description: Read timeout duration." + example: "10s" + - name: handshake_version + type: number + required: false + description: Handshake version for RethinkDB." + example: 1 + - name: keep_alive_timeout + type: string + required: false + description: Keep alive period duration." + example: "30s" + - name: max_idle + type: number + required: false + description: Maximum number of idle connections." + example: 5 + - name: auth_key + type: string + required: false + description: The authentication key for RethinkDB. This field is now deprecated." + example: "auth-key" + sensitive: true + - name: initial_cap + type: number + required: false + description: Initial connection pool capacity." + example: 5 + - name: max_open + type: number + required: false + description: Maximum number of open connections." + example: 10 + - name: discover_hosts + type: bool + required: false + description: Whether to discover hosts." + example: false + - name: node_refresh_interval + type: string + required: false + description: Node refresh interval duration." + example: "5m" \ No newline at end of file diff --git a/bindings/rethinkdb/statechange/statechange.go b/bindings/rethinkdb/statechange/statechange.go index 8f53c9cc52..7c437ef335 100644 --- a/bindings/rethinkdb/statechange/statechange.go +++ b/bindings/rethinkdb/statechange/statechange.go @@ -107,7 +107,7 @@ func (b *Binding) Read(ctx context.Context, handler bindings.Handler) error { go func() { defer b.wg.Done() for readCtx.Err() == nil { - var change interface{} + var change any ok := cursor.Next(&change) if !ok { b.logger.Errorf("error detecting change: %v", cursor.Err()) @@ -149,7 +149,7 @@ func (b *Binding) Close() error { return b.session.Close() } -func metadataToConfig(cfg map[string]string, logger logger.Logger) (StateConfig, error) { +func metadataToConfig(cfg map[string]string, _ logger.Logger) (StateConfig, error) { c := StateConfig{} // prepare metadata keys for decoding diff --git a/bindings/twilio/sendgrid/metadata.yaml b/bindings/twilio/sendgrid/metadata.yaml index f7873eeb83..d756d47c4a 100644 --- a/bindings/twilio/sendgrid/metadata.yaml +++ b/bindings/twilio/sendgrid/metadata.yaml @@ -52,4 +52,12 @@ metadata: - name: dynamicTemplateId required: false description: "The dynamic template ID" - example: '"your-template-id"' \ No newline at end of file + example: '"your-template-id"' + - name: emailFromName + required: false + description: "The sender name" + example: '"John Doe"' + - name: emailToName + required: false + description: "The recipient name" + example: '"Jane Smith"' \ No newline at end of file diff --git a/lock/redis/metadata.yaml b/lock/redis/metadata.yaml index a32a4901a2..41b713f0c9 100644 --- a/lock/redis/metadata.yaml +++ b/lock/redis/metadata.yaml @@ -19,11 +19,42 @@ authenticationProfiles: description: | Authenticate using Redis password. metadata: + - name: redisUsername + required: false + description: "The Redis username" + example: '"redis-user"' - name: redisPassword required: false sensitive: true description: "The Redis password" example: '"your-redis-password"' + - title: "Sentinel Authentication" + description: | + Authenticate using Redis Sentinel password. + metadata: + - name: sentinelUsername + required: false + description: "The Redis Sentinel username" + example: '"sentinel-user"' + - name: sentinelPassword + required: false + sensitive: true + description: "The Redis Sentinel password" + example: '"sentinel-password"' + - title: "TLS Authentication" + description: | + Authenticate using Redis TLS certificate. + metadata: + - name: clientCert + required: false + sensitive: true + description: "The Redis client certificate" + example: '"-----BEGIN CERTIFICATE-----\nXXX..."' + - name: clientKey + required: false + sensitive: true + description: "The Redis client key" + example: '"-----BEGIN PRIVATE KEY-----\nXXX..."' metadata: - name: redisHost required: true diff --git a/pubsub/rocketmq/metadata.yaml b/pubsub/rocketmq/metadata.yaml index d844b50cd4..93b647a066 100644 --- a/pubsub/rocketmq/metadata.yaml +++ b/pubsub/rocketmq/metadata.yaml @@ -189,4 +189,14 @@ metadata: type: string required: false description: The message properties to pass to the application (comma-separated). - example: "UNIQ_KEY,MSG_ID" \ No newline at end of file + example: "UNIQ_KEY,MSG_ID" + - name: groupName + type: string + required: false + description: The consumer group name (deprecated, use consumerGroup instead). + example: "dapr-consumer-group" + - name: sendTimeOut + type: number + required: false + description: The send timeout in nanoseconds (deprecated, use sendTimeOutSec instead). + example: 10000000000 \ No newline at end of file diff --git a/state/gcp/firestore/metadata.yaml b/state/gcp/firestore/metadata.yaml index 0a1f3ef13b..ce7aed3851 100644 --- a/state/gcp/firestore/metadata.yaml +++ b/state/gcp/firestore/metadata.yaml @@ -80,4 +80,20 @@ metadata: required: false description: The entity kind (collection name) for storing state data. example: "dapr-state" - default: "DaprState" \ No newline at end of file + default: "DaprState" + - name: type + type: string + required: false + description: The type of service account. + example: "service_account" + - name: connectionEndpoint + type: string + required: false + description: The Firestore connection endpoint. + example: "https://firestore.googleapis.com" + - name: noIndex + type: bool + required: false + description: Whether to disable indexing for the entity. + example: false + default: false \ No newline at end of file diff --git a/state/hazelcast/hazelcast.go b/state/hazelcast/hazelcast.go index 2144942eab..9ef11e08a9 100644 --- a/state/hazelcast/hazelcast.go +++ b/state/hazelcast/hazelcast.go @@ -40,8 +40,8 @@ type Hazelcast struct { } type hazelcastMetadata struct { - HazelcastServers string `json:"hazelcastServers" mapstructure:"hazelcastServers,servers"` - HazelcastMap string `json:"hazelcastMap" mapstructure:"hazelcastMap,map"` + HazelcastServers string `json:"hazelcastServers" mapstructure:"hazelcastServers"` + HazelcastMap string `json:"hazelcastMap" mapstructure:"hazelcastMap"` } // NewHazelcastStore returns a new hazelcast backed state store. diff --git a/state/hazelcast/metadata.yaml b/state/hazelcast/metadata.yaml index e30ac89d56..a8bd2822c0 100644 --- a/state/hazelcast/metadata.yaml +++ b/state/hazelcast/metadata.yaml @@ -9,12 +9,12 @@ urls: - title: Reference url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-hazelcast/ metadata: - - name: servers + - name: hazelcastServers type: string required: true description: Comma-separated list of Hazelcast servers. example: "localhost:5701" - - name: map + - name: hazelcastMap type: string required: true description: The Hazelcast map name. diff --git a/state/oci/objectstorage/metadata.yaml b/state/oci/objectstorage/metadata.yaml index 68c0dd26cf..4c43af36b4 100644 --- a/state/oci/objectstorage/metadata.yaml +++ b/state/oci/objectstorage/metadata.yaml @@ -76,4 +76,9 @@ metadata: type: string required: true description: The OCID of the compartment containing the bucket. - example: "ocid1.compartment.oc1..example" \ No newline at end of file + example: "ocid1.compartment.oc1..example" + - name: namespace + type: string + required: false + description: The OCI namespace for the object storage. + example: "my-namespace" \ No newline at end of file diff --git a/state/oci/objectstorage/objectstorage.go b/state/oci/objectstorage/objectstorage.go index 17a163f431..147ef472c9 100644 --- a/state/oci/objectstorage/objectstorage.go +++ b/state/oci/objectstorage/objectstorage.go @@ -51,11 +51,9 @@ const ( privateKeyKey = "privateKey" userKey = "userOCID" bucketNameKey = "bucketName" - // TODO: this needs to be used or removed below! - metadataTTLKey = "ttlInSeconds" - daprStateStoreMetaLabel = "dapr-state-store" - expiryTimeMetaLabel = "expiry-time-from-ttl" - isoDateTimeFormat = "2006-01-02T15:04:05" + daprStateStoreMetaLabel = "dapr-state-store" + expiryTimeMetaLabel = "expiry-time-from-ttl" + isoDateTimeFormat = "2006-01-02T15:04:05" ) type StateStore struct { @@ -81,7 +79,7 @@ type objectStoreMetadata struct { InstancePrincipalAuthentication bool ConfigFileAuthentication bool - OCIObjectStorageClient *objectstorage.ObjectStorageClient + OCIObjectStorageClient *objectstorage.ObjectStorageClient `mapstructure:"-"` } type objectStoreClient interface { diff --git a/state/rethinkdb/metadata.yaml b/state/rethinkdb/metadata.yaml index badc23f6d9..8c85167659 100644 --- a/state/rethinkdb/metadata.yaml +++ b/state/rethinkdb/metadata.yaml @@ -78,4 +78,55 @@ metadata: required: false description: Whether to enable opentracing for queries. example: false - default: false \ No newline at end of file + default: false + - name: write_timeout + type: string + required: false + description: Write timeout duration." + example: "10s" + - name: read_timeout + type: string + required: false + description: Read timeout duration." + example: "10s" + - name: handshake_version + type: number + required: false + description: Handshake version for RethinkDB." + example: 1 + - name: keep_alive_timeout + type: string + required: false + description: Keep alive period duration." + example: "30s" + - name: max_idle + type: number + required: false + description: Maximum number of idle connections." + example: 5 + - name: auth_key + type: string + required: false + description: The authentication key for RethinkDB." + example: "auth-key" + sensitive: true + - name: initial_cap + type: number + required: false + description: Initial connection pool capacity." + example: 5 + - name: max_open + type: number + required: false + description: Maximum number of open connections." + example: 10 + - name: discover_hosts + type: bool + required: false + description: Whether to discover hosts." + example: false + - name: node_refresh_interval + type: string + required: false + description: Node refresh interval duration." + example: "5m" \ No newline at end of file From 7e7a7ae5f9e64ed41113d7386db8442a535f042c Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Fri, 8 Aug 2025 09:15:10 -0500 Subject: [PATCH 15/32] fix: make metadata analyzer happy Signed-off-by: Samantha Coyle --- bindings/gcp/pubsub/metadata.yaml | 95 +++++++++++++++++++- bindings/gcp/pubsub/pubsub.go | 2 + bindings/rethinkdb/statechange/metadata.yaml | 20 ++--- state/rethinkdb/metadata.yaml | 20 ++--- 4 files changed, 116 insertions(+), 21 deletions(-) diff --git a/bindings/gcp/pubsub/metadata.yaml b/bindings/gcp/pubsub/metadata.yaml index 9927ebe2a1..2bd8db31e0 100644 --- a/bindings/gcp/pubsub/metadata.yaml +++ b/bindings/gcp/pubsub/metadata.yaml @@ -18,6 +18,58 @@ binding: description: "Receive messages from Pub/Sub subscription" builtinAuthenticationProfiles: - name: "gcp" +authenticationProfiles: + - title: "Service Account Authentication" + description: "Authenticate using a GCP service account JSON key." + metadata: + - name: projectID + type: string + required: true + description: The GCP project ID. + example: "my-project" + - name: privateKeyID + type: string + required: true + description: The private key ID from the service account JSON. + example: "abc123def456" + - name: privateKey + type: string + required: true + description: The private key from the service account JSON. + example: | + -----BEGIN PRIVATE KEY----- + XXX + -----END PRIVATE KEY----- + - name: clientEmail + type: string + required: true + description: The client email from the service account JSON. + example: "pubsub@my-project.iam.gserviceaccount.com" + - name: clientID + type: string + required: true + description: The client ID from the service account JSON. + example: "123456789012345678901" + - name: authURI + type: string + required: true + description: The authentication URI from the service account JSON. + example: "https://accounts.google.com/o/oauth2/auth" + - name: tokenURI + type: string + required: true + description: The token URI from the service account JSON. + example: "https://oauth2.googleapis.com/token" + - name: authProviderX509CertURL + type: string + required: true + description: The auth provider certificate URL from the service account JSON. + example: "https://www.googleapis.com/oauth2/v1/certs" + - name: clientX509CertURL + type: string + required: true + description: The client certificate URL from the service account JSON. + example: "https://www.googleapis.com/robot/v1/metadata/x509/pubsub%40my-project.iam.gserviceaccount.com" metadata: - name: topic required: true @@ -26,4 +78,45 @@ metadata: - name: subscription required: false description: "The Pub/Sub subscription name" - example: '"my-subscription"' \ No newline at end of file + example: '"my-subscription"' + - name: Type + required: false + description: "The type of service account" + example: '"service_account"' + - name: PrivateKey + required: false + description: "The private key for service account authentication" + example: '"-----BEGIN PRIVATE KEY-----\nXXX..."' + sensitive: true + - name: TokenURI + required: false + description: "The token URI for authentication" + example: '"https://oauth2.googleapis.com/token"' + - name: AuthProviderX509CertURL + required: false + description: "The auth provider X509 certificate URL" + example: '"https://www.googleapis.com/oauth2/v1/certs"' + - name: ProjectID + required: false + description: "The GCP project ID" + example: '"my-project-id"' + - name: PrivateKeyID + required: false + description: "The private key ID" + example: '"key-id"' + - name: ClientEmail + required: false + description: "The client email for service account" + example: '"service-account@my-project.iam.gserviceaccount.com"' + - name: ClientID + required: false + description: "The client ID" + example: '"client-id"' + - name: AuthURI + required: false + description: "The auth URI" + example: '"https://accounts.google.com/o/oauth2/auth"' + - name: ClientX509CertURL + required: false + description: "The client X509 certificate URL" + example: '"https://www.googleapis.com/robot/v1/metadata/x509/service-account%40my-project.iam.gserviceaccount.com"' \ No newline at end of file diff --git a/bindings/gcp/pubsub/pubsub.go b/bindings/gcp/pubsub/pubsub.go index 683956c89e..7e9c849d40 100644 --- a/bindings/gcp/pubsub/pubsub.go +++ b/bindings/gcp/pubsub/pubsub.go @@ -47,6 +47,8 @@ type GCPPubSub struct { } // These JSON tags directly match the builtin auth provider metadata fields for GCP. +// TODO: in future, this needs to use the same setup that pubsub gcp pubsub component uses, +// so we can embed the builtin auth profile instead... type pubSubMetadata struct { Topic string `json:"topic"` Subscription string `json:"subscription"` diff --git a/bindings/rethinkdb/statechange/metadata.yaml b/bindings/rethinkdb/statechange/metadata.yaml index 78b24d3e71..8cb3de1f2e 100644 --- a/bindings/rethinkdb/statechange/metadata.yaml +++ b/bindings/rethinkdb/statechange/metadata.yaml @@ -85,53 +85,53 @@ metadata: description: Whether to enable opentracing for queries. example: false default: false - - name: write_timeout + - name: writeTimeout type: string required: false description: Write timeout duration." example: "10s" - - name: read_timeout + - name: readTimeout type: string required: false description: Read timeout duration." example: "10s" - - name: handshake_version + - name: handshakeVersion type: number required: false description: Handshake version for RethinkDB." example: 1 - - name: keep_alive_timeout + - name: keepAlivePeriod type: string required: false description: Keep alive period duration." example: "30s" - - name: max_idle + - name: maxIdle type: number required: false description: Maximum number of idle connections." example: 5 - - name: auth_key + - name: authKey type: string required: false description: The authentication key for RethinkDB. This field is now deprecated." example: "auth-key" sensitive: true - - name: initial_cap + - name: initialCap type: number required: false description: Initial connection pool capacity." example: 5 - - name: max_open + - name: maxOpen type: number required: false description: Maximum number of open connections." example: 10 - - name: discover_hosts + - name: discoverHosts type: bool required: false description: Whether to discover hosts." example: false - - name: node_refresh_interval + - name: nodeRefreshInterval type: string required: false description: Node refresh interval duration." diff --git a/state/rethinkdb/metadata.yaml b/state/rethinkdb/metadata.yaml index 8c85167659..aa5e2005ac 100644 --- a/state/rethinkdb/metadata.yaml +++ b/state/rethinkdb/metadata.yaml @@ -79,53 +79,53 @@ metadata: description: Whether to enable opentracing for queries. example: false default: false - - name: write_timeout + - name: writeTimeout type: string required: false description: Write timeout duration." example: "10s" - - name: read_timeout + - name: readTimeout type: string required: false description: Read timeout duration." example: "10s" - - name: handshake_version + - name: handshakeVersion type: number required: false description: Handshake version for RethinkDB." example: 1 - - name: keep_alive_timeout + - name: keepAlivePeriod type: string required: false description: Keep alive period duration." example: "30s" - - name: max_idle + - name: maxIdle type: number required: false description: Maximum number of idle connections." example: 5 - - name: auth_key + - name: authKey type: string required: false description: The authentication key for RethinkDB." example: "auth-key" sensitive: true - - name: initial_cap + - name: initialCap type: number required: false description: Initial connection pool capacity." example: 5 - - name: max_open + - name: maxOpen type: number required: false description: Maximum number of open connections." example: 10 - - name: discover_hosts + - name: discoverHosts type: bool required: false description: Whether to discover hosts." example: false - - name: node_refresh_interval + - name: nodeRefreshInterval type: string required: false description: Node refresh interval duration." From 2842f7a7f44c3e1fafdb20a18f26d4ec9bcc0900 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Fri, 8 Aug 2025 09:35:08 -0500 Subject: [PATCH 16/32] fix(rethink): support tls config with a workaround to make metadata analyzer happy Signed-off-by: Samantha Coyle --- bindings/rethinkdb/statechange/metadata.yaml | 20 +++++ bindings/rethinkdb/statechange/statechange.go | 86 +++++++++++++++++- state/rethinkdb/metadata.yaml | 20 +++++ state/rethinkdb/rethinkdb.go | 88 ++++++++++++++++++- 4 files changed, 207 insertions(+), 7 deletions(-) diff --git a/bindings/rethinkdb/statechange/metadata.yaml b/bindings/rethinkdb/statechange/metadata.yaml index 8cb3de1f2e..a995d32a13 100644 --- a/bindings/rethinkdb/statechange/metadata.yaml +++ b/bindings/rethinkdb/statechange/metadata.yaml @@ -44,6 +44,26 @@ authenticationProfiles: required: false description: The password for authentication. This is only used for v1 handshake protocol. example: "password" + - title: "TLS Authentication" + description: "Authenticate using client certificate and key." + metadata: + - name: enableTLS + type: bool + required: false + description: Whether to enable TLS encryption. + example: false + default: false + - name: clientCert + type: string + required: true + description: The client certificate for TLS authentication. + example: "-----BEGIN CERTIFICATE-----\nXXX..."" + - name: clientKey + type: string + required: true + description: The client key for TLS authentication. + example: "-----BEGIN PRIVATE KEY-----\nXXX..."" + sensitive: true metadata: - name: table type: string diff --git a/bindings/rethinkdb/statechange/statechange.go b/bindings/rethinkdb/statechange/statechange.go index 7c437ef335..56677d9174 100644 --- a/bindings/rethinkdb/statechange/statechange.go +++ b/bindings/rethinkdb/statechange/statechange.go @@ -15,6 +15,7 @@ package statechange import ( "context" + "crypto/tls" "encoding/json" "errors" "fmt" @@ -22,6 +23,7 @@ import ( "strings" "sync" "sync/atomic" + "time" r "github.com/dancannon/gorethink" @@ -44,8 +46,37 @@ type Binding struct { // StateConfig is the binding config. type StateConfig struct { - r.ConnectOpts `mapstructure:",squash"` - Table string `mapstructure:"table"` + ConnectOptsWrapper `mapstructure:",squash"` + Table string `mapstructure:"table"` +} + +// ConnectOptsWrapper wraps r.ConnectOpts but excludes TLSConfig +// This is needed because the metadata decoder does not support nested structs with tags as inputs in the metadata.yaml file +type ConnectOptsWrapper struct { + Address string `gorethink:"address,omitempty"` + Addresses []string `gorethink:"addresses,omitempty"` + Database string `gorethink:"database,omitempty"` + Username string `gorethink:"username,omitempty"` + Password string `gorethink:"password,omitempty"` + AuthKey string `gorethink:"authkey,omitempty"` + Timeout time.Duration `gorethink:"timeout,omitempty"` + WriteTimeout time.Duration `gorethink:"write_timeout,omitempty"` + ReadTimeout time.Duration `gorethink:"read_timeout,omitempty"` + KeepAlivePeriod time.Duration `gorethink:"keep_alive_timeout,omitempty"` + HandshakeVersion int `gorethink:"handshake_version,omitempty"` + MaxIdle int `gorethink:"max_idle,omitempty"` + InitialCap int `gorethink:"initial_cap,omitempty"` + MaxOpen int `gorethink:"max_open,omitempty"` + DiscoverHosts bool `gorethink:"discover_hosts,omitempty"` + NodeRefreshInterval time.Duration `gorethink:"node_refresh_interval,omitempty"` + UseJSONNumber bool `gorethink:"use_json_number,omitempty"` + NumRetries int `gorethink:"num_retries,omitempty"` + HostDecayDuration time.Duration `gorethink:"host_decay_duration,omitempty"` + UseOpentracing bool `gorethink:"use_opentracing,omitempty"` + // TLS fields must be brought in as separate fields as they will not be processed by the metadata decoder properly without this + EnableTLS bool `gorethink:"enable_tls,omitempty"` + ClientCert string `gorethink:"client_cert,omitempty"` + ClientKey string `gorethink:"client_key,omitempty"` } // NewRethinkDBStateChangeBinding returns a new RethinkDB actor event input binding. @@ -64,7 +95,40 @@ func (b *Binding) Init(ctx context.Context, metadata bindings.Metadata) error { } b.config = cfg - ses, err := r.Connect(b.config.ConnectOpts) + // Convert wrapper to r.ConnectOpts + connectOpts := r.ConnectOpts{ + Address: cfg.Address, + Addresses: cfg.Addresses, + Database: cfg.Database, + Username: cfg.Username, + Password: cfg.Password, + AuthKey: cfg.AuthKey, + Timeout: cfg.Timeout, + WriteTimeout: cfg.WriteTimeout, + ReadTimeout: cfg.ReadTimeout, + KeepAlivePeriod: cfg.KeepAlivePeriod, + HandshakeVersion: r.HandshakeVersion(cfg.HandshakeVersion), + MaxIdle: cfg.MaxIdle, + InitialCap: cfg.InitialCap, + MaxOpen: cfg.MaxOpen, + DiscoverHosts: cfg.DiscoverHosts, + NodeRefreshInterval: cfg.NodeRefreshInterval, + UseJSONNumber: cfg.UseJSONNumber, + NumRetries: cfg.NumRetries, + HostDecayDuration: cfg.HostDecayDuration, + UseOpentracing: cfg.UseOpentracing, + } + + // Configure TLS if enabled + if cfg.EnableTLS { + tlsConfig, err := createTLSConfig(cfg.ClientCert, cfg.ClientKey) + if err != nil { + return fmt.Errorf("error creating TLS config: %w", err) + } + connectOpts.TLSConfig = tlsConfig + } + + ses, err := r.Connect(connectOpts) if err != nil { return fmt.Errorf("error connecting to the database: %w", err) } @@ -73,6 +137,22 @@ func (b *Binding) Init(ctx context.Context, metadata bindings.Metadata) error { return nil } +// createTLSConfig creates a tls.Config from client certificate and key +func createTLSConfig(clientCert, clientKey string) (*tls.Config, error) { + if clientCert == "" || clientKey == "" { + return nil, fmt.Errorf("both client certificate and key are required for TLS") + } + + cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey)) + if err != nil { + return nil, fmt.Errorf("error parsing client certificate and key: %w", err) + } + + return &tls.Config{ + Certificates: []tls.Certificate{cert}, + }, nil +} + // Read triggers the RethinkDB scheduler. func (b *Binding) Read(ctx context.Context, handler bindings.Handler) error { if b.closed.Load() { diff --git a/state/rethinkdb/metadata.yaml b/state/rethinkdb/metadata.yaml index aa5e2005ac..6a62b083f4 100644 --- a/state/rethinkdb/metadata.yaml +++ b/state/rethinkdb/metadata.yaml @@ -38,6 +38,26 @@ authenticationProfiles: required: false description: The password for authentication. This is only used for v1 handshake protocol. example: "password" + - title: "TLS Authentication" + description: "Authenticate using client certificate and key." + metadata: + - name: enableTLS + type: bool + required: false + description: Whether to enable TLS encryption. + example: false + default: false + - name: clientCert + type: string + required: true + description: The client certificate for TLS authentication. + example: "-----BEGIN CERTIFICATE-----\nXXX..."" + - name: clientKey + type: string + required: true + description: The client key for TLS authentication. + example: "-----BEGIN PRIVATE KEY-----\nXXX..."" + sensitive: true metadata: - name: table type: string diff --git a/state/rethinkdb/rethinkdb.go b/state/rethinkdb/rethinkdb.go index 11c3251404..aa6c5838fe 100644 --- a/state/rethinkdb/rethinkdb.go +++ b/state/rethinkdb/rethinkdb.go @@ -15,6 +15,7 @@ package rethinkdb import ( "context" + "crypto/tls" "encoding/json" "errors" "fmt" @@ -50,9 +51,38 @@ type RethinkDB struct { } type stateConfig struct { - r.ConnectOpts `mapstructure:",squash"` - Archive bool `json:"archive"` - Table string `json:"table"` + ConnectOptsWrapper `mapstructure:",squash"` + Archive bool `json:"archive"` + Table string `json:"table"` +} + +// ConnectOptsWrapper wraps r.ConnectOpts but excludes TLSConfig +// This is needed because the metadata decoder does not support nested structs with tags as inputs in the metadata.yaml file +type ConnectOptsWrapper struct { + Address string `gorethink:"address,omitempty"` + Addresses []string `gorethink:"addresses,omitempty"` + Database string `gorethink:"database,omitempty"` + Username string `gorethink:"username,omitempty"` + Password string `gorethink:"password,omitempty"` + AuthKey string `gorethink:"authkey,omitempty"` + Timeout time.Duration `gorethink:"timeout,omitempty"` + WriteTimeout time.Duration `gorethink:"write_timeout,omitempty"` + ReadTimeout time.Duration `gorethink:"read_timeout,omitempty"` + KeepAlivePeriod time.Duration `gorethink:"keep_alive_timeout,omitempty"` + HandshakeVersion int `gorethink:"handshake_version,omitempty"` + MaxIdle int `gorethink:"max_idle,omitempty"` + InitialCap int `gorethink:"initial_cap,omitempty"` + MaxOpen int `gorethink:"max_open,omitempty"` + DiscoverHosts bool `gorethink:"discover_hosts,omitempty"` + NodeRefreshInterval time.Duration `gorethink:"node_refresh_interval,omitempty"` + UseJSONNumber bool `gorethink:"use_json_number,omitempty"` + NumRetries int `gorethink:"num_retries,omitempty"` + HostDecayDuration time.Duration `gorethink:"host_decay_duration,omitempty"` + UseOpentracing bool `gorethink:"use_opentracing,omitempty"` + // TLS fields must be brought in as separate fields as they will not be processed by the metadata decoder properly without this + EnableTLS bool `gorethink:"enable_tls,omitempty"` + ClientCert string `gorethink:"client_cert,omitempty"` + ClientKey string `gorethink:"client_key,omitempty"` } type stateRecord struct { @@ -84,7 +114,41 @@ func (s *RethinkDB) Init(ctx context.Context, metadata state.Metadata) error { if s.session != nil && s.session.IsConnected() { s.session.Close() } - ses, err := r.Connect(cfg.ConnectOpts) + + // Convert wrapper to r.ConnectOpts + connectOpts := r.ConnectOpts{ + Address: cfg.Address, + Addresses: cfg.Addresses, + Database: cfg.Database, + Username: cfg.Username, + Password: cfg.Password, + AuthKey: cfg.AuthKey, + Timeout: cfg.Timeout, + WriteTimeout: cfg.WriteTimeout, + ReadTimeout: cfg.ReadTimeout, + KeepAlivePeriod: cfg.KeepAlivePeriod, + HandshakeVersion: r.HandshakeVersion(cfg.HandshakeVersion), + MaxIdle: cfg.MaxIdle, + InitialCap: cfg.InitialCap, + MaxOpen: cfg.MaxOpen, + DiscoverHosts: cfg.DiscoverHosts, + NodeRefreshInterval: cfg.NodeRefreshInterval, + UseJSONNumber: cfg.UseJSONNumber, + NumRetries: cfg.NumRetries, + HostDecayDuration: cfg.HostDecayDuration, + UseOpentracing: cfg.UseOpentracing, + } + + // Configure TLS if enabled + if cfg.EnableTLS { + tlsConfig, err := createTLSConfig(cfg.ClientCert, cfg.ClientKey) + if err != nil { + return fmt.Errorf("error creating TLS config: %w", err) + } + connectOpts.TLSConfig = tlsConfig + } + + ses, err := r.Connect(connectOpts) if err != nil { return fmt.Errorf("error connecting to the database: %w", err) } @@ -310,6 +374,22 @@ func metadataToConfig(cfg map[string]string, logger logger.Logger) (*stateConfig return &c, nil } +// createTLSConfig creates a tls.Config from client certificate and key +func createTLSConfig(clientCert, clientKey string) (*tls.Config, error) { + if clientCert == "" || clientKey == "" { + return nil, fmt.Errorf("both client certificate and key are required for TLS") + } + + cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey)) + if err != nil { + return nil, fmt.Errorf("error parsing client certificate and key: %w", err) + } + + return &tls.Config{ + Certificates: []tls.Certificate{cert}, + }, nil +} + func (s *RethinkDB) GetComponentMetadata() (metadataInfo metadata.MetadataMap) { metadataStruct := stateConfig{} metadata.GetMetadataInfoFromStructType(reflect.TypeOf(metadataStruct), &metadataInfo, metadata.StateStoreType) From 0b616eff626a4a77360aec2fadbbef74f35dc242 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Fri, 8 Aug 2025 09:40:35 -0500 Subject: [PATCH 17/32] style: rm extra quote Signed-off-by: Samantha Coyle --- bindings/rethinkdb/statechange/metadata.yaml | 4 ++-- state/rethinkdb/metadata.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bindings/rethinkdb/statechange/metadata.yaml b/bindings/rethinkdb/statechange/metadata.yaml index a995d32a13..68a72cd378 100644 --- a/bindings/rethinkdb/statechange/metadata.yaml +++ b/bindings/rethinkdb/statechange/metadata.yaml @@ -57,12 +57,12 @@ authenticationProfiles: type: string required: true description: The client certificate for TLS authentication. - example: "-----BEGIN CERTIFICATE-----\nXXX..."" + example: "-----BEGIN CERTIFICATE-----\nXXX..." - name: clientKey type: string required: true description: The client key for TLS authentication. - example: "-----BEGIN PRIVATE KEY-----\nXXX..."" + example: "-----BEGIN PRIVATE KEY-----\nXXX..." sensitive: true metadata: - name: table diff --git a/state/rethinkdb/metadata.yaml b/state/rethinkdb/metadata.yaml index 6a62b083f4..26edf5c0c2 100644 --- a/state/rethinkdb/metadata.yaml +++ b/state/rethinkdb/metadata.yaml @@ -51,12 +51,12 @@ authenticationProfiles: type: string required: true description: The client certificate for TLS authentication. - example: "-----BEGIN CERTIFICATE-----\nXXX..."" + example: "-----BEGIN CERTIFICATE-----\nXXX..." - name: clientKey type: string required: true description: The client key for TLS authentication. - example: "-----BEGIN PRIVATE KEY-----\nXXX..."" + example: "-----BEGIN PRIVATE KEY-----\nXXX..." sensitive: true metadata: - name: table From 443536ff5ce29c41def65bcc53581f2950876d59 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Fri, 8 Aug 2025 09:51:31 -0500 Subject: [PATCH 18/32] style: make linter happy Signed-off-by: Samantha Coyle --- bindings/rethinkdb/statechange/statechange.go | 9 +++++---- cryptography/subtlecrypto.go | 3 ++- state/rethinkdb/rethinkdb.go | 9 +++++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/bindings/rethinkdb/statechange/statechange.go b/bindings/rethinkdb/statechange/statechange.go index 56677d9174..74e7bbba29 100644 --- a/bindings/rethinkdb/statechange/statechange.go +++ b/bindings/rethinkdb/statechange/statechange.go @@ -121,9 +121,9 @@ func (b *Binding) Init(ctx context.Context, metadata bindings.Metadata) error { // Configure TLS if enabled if cfg.EnableTLS { - tlsConfig, err := createTLSConfig(cfg.ClientCert, cfg.ClientKey) - if err != nil { - return fmt.Errorf("error creating TLS config: %w", err) + tlsConfig, tlsErr := createTLSConfig(cfg.ClientCert, cfg.ClientKey) + if tlsErr != nil { + return fmt.Errorf("error creating TLS config: %w", tlsErr) } connectOpts.TLSConfig = tlsConfig } @@ -140,7 +140,7 @@ func (b *Binding) Init(ctx context.Context, metadata bindings.Metadata) error { // createTLSConfig creates a tls.Config from client certificate and key func createTLSConfig(clientCert, clientKey string) (*tls.Config, error) { if clientCert == "" || clientKey == "" { - return nil, fmt.Errorf("both client certificate and key are required for TLS") + return nil, errors.New("both client certificate and key are required for TLS") } cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey)) @@ -150,6 +150,7 @@ func createTLSConfig(clientCert, clientKey string) (*tls.Config, error) { return &tls.Config{ Certificates: []tls.Certificate{cert}, + MinVersion: tls.VersionTLS12, }, nil } diff --git a/cryptography/subtlecrypto.go b/cryptography/subtlecrypto.go index d3c5d9193e..181e3cf6d2 100644 --- a/cryptography/subtlecrypto.go +++ b/cryptography/subtlecrypto.go @@ -17,8 +17,9 @@ import ( "context" "io" - "github.com/dapr/components-contrib/metadata" "github.com/lestrrat-go/jwx/v2/jwk" + + "github.com/dapr/components-contrib/metadata" ) // SubtleCrypto offers an interface to perform low-level ("subtle") cryptographic operations with keys stored in a vault. diff --git a/state/rethinkdb/rethinkdb.go b/state/rethinkdb/rethinkdb.go index aa6c5838fe..0502ebbcd8 100644 --- a/state/rethinkdb/rethinkdb.go +++ b/state/rethinkdb/rethinkdb.go @@ -141,9 +141,9 @@ func (s *RethinkDB) Init(ctx context.Context, metadata state.Metadata) error { // Configure TLS if enabled if cfg.EnableTLS { - tlsConfig, err := createTLSConfig(cfg.ClientCert, cfg.ClientKey) - if err != nil { - return fmt.Errorf("error creating TLS config: %w", err) + tlsConfig, tlsErr := createTLSConfig(cfg.ClientCert, cfg.ClientKey) + if tlsErr != nil { + return fmt.Errorf("error creating TLS config: %w", tlsErr) } connectOpts.TLSConfig = tlsConfig } @@ -377,7 +377,7 @@ func metadataToConfig(cfg map[string]string, logger logger.Logger) (*stateConfig // createTLSConfig creates a tls.Config from client certificate and key func createTLSConfig(clientCert, clientKey string) (*tls.Config, error) { if clientCert == "" || clientKey == "" { - return nil, fmt.Errorf("both client certificate and key are required for TLS") + return nil, errors.New("both client certificate and key are required for TLS") } cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey)) @@ -387,6 +387,7 @@ func createTLSConfig(clientCert, clientKey string) (*tls.Config, error) { return &tls.Config{ Certificates: []tls.Certificate{cert}, + MinVersion: tls.VersionTLS12, }, nil } From dd8ee7e2084c573e143323c2bcf88c6227f2ad4b Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Fri, 8 Aug 2025 10:27:13 -0500 Subject: [PATCH 19/32] style: go back to old crypto nameing Signed-off-by: Samantha Coyle --- .build-tools/cmd/cmd-check-component-registrations.go | 5 +++-- {cryptography => crypto}/azure/keyvault/algorithms.go | 0 {cryptography => crypto}/azure/keyvault/component.go | 2 +- {cryptography => crypto}/azure/keyvault/jwk.go | 2 +- {cryptography => crypto}/azure/keyvault/metadata.go | 2 +- {cryptography => crypto}/azure/keyvault/metadata.yaml | 0 {cryptography => crypto}/feature.go | 0 {cryptography => crypto}/jwks/component.go | 2 +- {cryptography => crypto}/jwks/metadata.go | 2 +- {cryptography => crypto}/jwks/metadata.yaml | 0 {cryptography => crypto}/key.go | 0 {cryptography => crypto}/kubernetes/secrets/component.go | 2 +- {cryptography => crypto}/kubernetes/secrets/metadata.go | 2 +- {cryptography => crypto}/kubernetes/secrets/metadata.yaml | 0 {cryptography => crypto}/local_base_component.go | 0 {cryptography => crypto}/localstorage/component.go | 2 +- {cryptography => crypto}/localstorage/metadata.go | 2 +- {cryptography => crypto}/localstorage/metadata.yaml | 0 {cryptography => crypto}/metadata.go | 0 {cryptography => crypto}/pubkey_cache.go | 0 {cryptography => crypto}/pubkey_cache_test.go | 0 {cryptography => crypto}/subtlecrypto.go | 0 tests/certification/embedded/components.go | 2 +- tests/conformance/crypto_test.go | 8 ++++---- tests/conformance/cryptography/crypto.go | 2 +- 25 files changed, 18 insertions(+), 17 deletions(-) rename {cryptography => crypto}/azure/keyvault/algorithms.go (100%) rename {cryptography => crypto}/azure/keyvault/component.go (99%) rename {cryptography => crypto}/azure/keyvault/jwk.go (98%) rename {cryptography => crypto}/azure/keyvault/metadata.go (97%) rename {cryptography => crypto}/azure/keyvault/metadata.yaml (100%) rename {cryptography => crypto}/feature.go (100%) rename {cryptography => crypto}/jwks/component.go (98%) rename {cryptography => crypto}/jwks/metadata.go (97%) rename {cryptography => crypto}/jwks/metadata.yaml (100%) rename {cryptography => crypto}/key.go (100%) rename {cryptography => crypto}/kubernetes/secrets/component.go (98%) rename {cryptography => crypto}/kubernetes/secrets/metadata.go (95%) rename {cryptography => crypto}/kubernetes/secrets/metadata.yaml (100%) rename {cryptography => crypto}/local_base_component.go (100%) rename {cryptography => crypto}/localstorage/component.go (98%) rename {cryptography => crypto}/localstorage/metadata.go (96%) rename {cryptography => crypto}/localstorage/metadata.yaml (100%) rename {cryptography => crypto}/metadata.go (100%) rename {cryptography => crypto}/pubkey_cache.go (100%) rename {cryptography => crypto}/pubkey_cache_test.go (100%) rename {cryptography => crypto}/subtlecrypto.go (100%) diff --git a/.build-tools/cmd/cmd-check-component-registrations.go b/.build-tools/cmd/cmd-check-component-registrations.go index 5dac069d2d..d1808c09f9 100644 --- a/.build-tools/cmd/cmd-check-component-registrations.go +++ b/.build-tools/cmd/cmd-check-component-registrations.go @@ -125,7 +125,8 @@ func checkCryptographyComponents() { ignoreContribComponents := []string{"pubkey_cache"} // TODO: in future rm the aliases with the dapr prefix in runtime to clean this up! ignoreDaprComponents := []string{"dapr.localstorage", "dapr.kubernetes.secrets", "dapr.jwks"} - checkComponents("cryptography", ignoreDaprComponents, ignoreContribComponents) + // TODO: in future update this to cryptography once we have a cryptography component in contrib and not crypto components + checkComponents("crypto", ignoreDaprComponents, ignoreContribComponents) } func checkNameResolutionComponents() { @@ -282,7 +283,7 @@ func findComponentsInBothRepos(componentType string, ignoreContribComponents []s excludeFiles = []string{"--exclude=envelope.go", "--exclude=responses.go"} case "bindings": excludeFiles = []string{"--exclude=client.go"} - case "cryptography": + case "crypto": excludeFiles = []string{"--exclude=key.go", "--exclude=pubkey_cache.go"} case "middleware": excludeFiles = []string{"--exclude=mock*"} diff --git a/cryptography/azure/keyvault/algorithms.go b/crypto/azure/keyvault/algorithms.go similarity index 100% rename from cryptography/azure/keyvault/algorithms.go rename to crypto/azure/keyvault/algorithms.go diff --git a/cryptography/azure/keyvault/component.go b/crypto/azure/keyvault/component.go similarity index 99% rename from cryptography/azure/keyvault/component.go rename to crypto/azure/keyvault/component.go index 537696db75..c3efb00cf2 100644 --- a/cryptography/azure/keyvault/component.go +++ b/crypto/azure/keyvault/component.go @@ -26,7 +26,7 @@ import ( "github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jwk" - contribCrypto "github.com/dapr/components-contrib/cryptography" + contribCrypto "github.com/dapr/components-contrib/crypto" contribMetadata "github.com/dapr/components-contrib/metadata" internals "github.com/dapr/kit/crypto" "github.com/dapr/kit/logger" diff --git a/cryptography/azure/keyvault/jwk.go b/crypto/azure/keyvault/jwk.go similarity index 98% rename from cryptography/azure/keyvault/jwk.go rename to crypto/azure/keyvault/jwk.go index d663b09035..eda8325b8a 100644 --- a/cryptography/azure/keyvault/jwk.go +++ b/crypto/azure/keyvault/jwk.go @@ -25,7 +25,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys" "github.com/lestrrat-go/jwx/v2/jwk" - contribCrypto "github.com/dapr/components-contrib/cryptography" + contribCrypto "github.com/dapr/components-contrib/crypto" ) // KeyBundleToKey converts an azkeys.KeyBundle object to a contribCrypto.Key object, containing only the public part of the asymmetric key. diff --git a/cryptography/azure/keyvault/metadata.go b/crypto/azure/keyvault/metadata.go similarity index 97% rename from cryptography/azure/keyvault/metadata.go rename to crypto/azure/keyvault/metadata.go index 0ef3350be1..b5d48cc529 100644 --- a/cryptography/azure/keyvault/metadata.go +++ b/crypto/azure/keyvault/metadata.go @@ -20,7 +20,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore" azauth "github.com/dapr/components-contrib/common/authentication/azure" - contribCrypto "github.com/dapr/components-contrib/cryptography" + contribCrypto "github.com/dapr/components-contrib/crypto" "github.com/dapr/kit/metadata" ) diff --git a/cryptography/azure/keyvault/metadata.yaml b/crypto/azure/keyvault/metadata.yaml similarity index 100% rename from cryptography/azure/keyvault/metadata.yaml rename to crypto/azure/keyvault/metadata.yaml diff --git a/cryptography/feature.go b/crypto/feature.go similarity index 100% rename from cryptography/feature.go rename to crypto/feature.go diff --git a/cryptography/jwks/component.go b/crypto/jwks/component.go similarity index 98% rename from cryptography/jwks/component.go rename to crypto/jwks/component.go index ffdc76bb7f..42a18d11d9 100644 --- a/cryptography/jwks/component.go +++ b/crypto/jwks/component.go @@ -23,7 +23,7 @@ import ( "github.com/lestrrat-go/jwx/v2/jwk" - contribCrypto "github.com/dapr/components-contrib/cryptography" + contribCrypto "github.com/dapr/components-contrib/crypto" contribMetadata "github.com/dapr/components-contrib/metadata" "github.com/dapr/kit/jwkscache" "github.com/dapr/kit/logger" diff --git a/cryptography/jwks/metadata.go b/crypto/jwks/metadata.go similarity index 97% rename from cryptography/jwks/metadata.go rename to crypto/jwks/metadata.go index 29a0181424..da384f568c 100644 --- a/cryptography/jwks/metadata.go +++ b/crypto/jwks/metadata.go @@ -17,7 +17,7 @@ import ( "errors" "time" - contribCrypto "github.com/dapr/components-contrib/cryptography" + contribCrypto "github.com/dapr/components-contrib/crypto" "github.com/dapr/kit/metadata" ) diff --git a/cryptography/jwks/metadata.yaml b/crypto/jwks/metadata.yaml similarity index 100% rename from cryptography/jwks/metadata.yaml rename to crypto/jwks/metadata.yaml diff --git a/cryptography/key.go b/crypto/key.go similarity index 100% rename from cryptography/key.go rename to crypto/key.go diff --git a/cryptography/kubernetes/secrets/component.go b/crypto/kubernetes/secrets/component.go similarity index 98% rename from cryptography/kubernetes/secrets/component.go rename to crypto/kubernetes/secrets/component.go index 06a99ab9ea..f6d2573470 100644 --- a/cryptography/kubernetes/secrets/component.go +++ b/crypto/kubernetes/secrets/component.go @@ -28,7 +28,7 @@ import ( "k8s.io/client-go/kubernetes" kubeclient "github.com/dapr/components-contrib/common/authentication/kubernetes" - contribCrypto "github.com/dapr/components-contrib/cryptography" + contribCrypto "github.com/dapr/components-contrib/crypto" "github.com/dapr/components-contrib/metadata" internals "github.com/dapr/kit/crypto" "github.com/dapr/kit/logger" diff --git a/cryptography/kubernetes/secrets/metadata.go b/crypto/kubernetes/secrets/metadata.go similarity index 95% rename from cryptography/kubernetes/secrets/metadata.go rename to crypto/kubernetes/secrets/metadata.go index c007c6636a..fc9471487a 100644 --- a/cryptography/kubernetes/secrets/metadata.go +++ b/crypto/kubernetes/secrets/metadata.go @@ -14,7 +14,7 @@ limitations under the License. package secrets import ( - contribCrypto "github.com/dapr/components-contrib/cryptography" + contribCrypto "github.com/dapr/components-contrib/crypto" "github.com/dapr/kit/metadata" ) diff --git a/cryptography/kubernetes/secrets/metadata.yaml b/crypto/kubernetes/secrets/metadata.yaml similarity index 100% rename from cryptography/kubernetes/secrets/metadata.yaml rename to crypto/kubernetes/secrets/metadata.yaml diff --git a/cryptography/local_base_component.go b/crypto/local_base_component.go similarity index 100% rename from cryptography/local_base_component.go rename to crypto/local_base_component.go diff --git a/cryptography/localstorage/component.go b/crypto/localstorage/component.go similarity index 98% rename from cryptography/localstorage/component.go rename to crypto/localstorage/component.go index c5fea75135..b633e3678e 100644 --- a/cryptography/localstorage/component.go +++ b/crypto/localstorage/component.go @@ -25,7 +25,7 @@ import ( "github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jwk" - contribCrypto "github.com/dapr/components-contrib/cryptography" + contribCrypto "github.com/dapr/components-contrib/crypto" contribMetadata "github.com/dapr/components-contrib/metadata" internals "github.com/dapr/kit/crypto" "github.com/dapr/kit/logger" diff --git a/cryptography/localstorage/metadata.go b/crypto/localstorage/metadata.go similarity index 96% rename from cryptography/localstorage/metadata.go rename to crypto/localstorage/metadata.go index de8b4ac319..f4d5a036f0 100644 --- a/cryptography/localstorage/metadata.go +++ b/crypto/localstorage/metadata.go @@ -19,7 +19,7 @@ import ( "os" "path/filepath" - contribCrypto "github.com/dapr/components-contrib/cryptography" + contribCrypto "github.com/dapr/components-contrib/crypto" "github.com/dapr/kit/metadata" ) diff --git a/cryptography/localstorage/metadata.yaml b/crypto/localstorage/metadata.yaml similarity index 100% rename from cryptography/localstorage/metadata.yaml rename to crypto/localstorage/metadata.yaml diff --git a/cryptography/metadata.go b/crypto/metadata.go similarity index 100% rename from cryptography/metadata.go rename to crypto/metadata.go diff --git a/cryptography/pubkey_cache.go b/crypto/pubkey_cache.go similarity index 100% rename from cryptography/pubkey_cache.go rename to crypto/pubkey_cache.go diff --git a/cryptography/pubkey_cache_test.go b/crypto/pubkey_cache_test.go similarity index 100% rename from cryptography/pubkey_cache_test.go rename to crypto/pubkey_cache_test.go diff --git a/cryptography/subtlecrypto.go b/crypto/subtlecrypto.go similarity index 100% rename from cryptography/subtlecrypto.go rename to crypto/subtlecrypto.go diff --git a/tests/certification/embedded/components.go b/tests/certification/embedded/components.go index 9a165099ab..4f2c7f3adc 100644 --- a/tests/certification/embedded/components.go +++ b/tests/certification/embedded/components.go @@ -18,7 +18,7 @@ import ( "github.com/dapr/kit/logger" // Name resolutions. - nrConsul "github.com/dapr/components-contrib/nameresolution/consul" + nrConsul "github.com/dapr/components-contrib/nameresolution/hashicorp/consul" nrKubernetes "github.com/dapr/components-contrib/nameresolution/kubernetes" nrMdns "github.com/dapr/components-contrib/nameresolution/mdns" diff --git a/tests/conformance/crypto_test.go b/tests/conformance/crypto_test.go index 618b9ba8dd..8447d4e292 100644 --- a/tests/conformance/crypto_test.go +++ b/tests/conformance/crypto_test.go @@ -22,10 +22,10 @@ import ( "github.com/stretchr/testify/require" - contribCrypto "github.com/dapr/components-contrib/cryptography" - cr_azurekeyvault "github.com/dapr/components-contrib/cryptography/azure/keyvault" - cr_jwks "github.com/dapr/components-contrib/cryptography/jwks" - cr_localstorage "github.com/dapr/components-contrib/cryptography/localstorage" + contribCrypto "github.com/dapr/components-contrib/crypto" + cr_azurekeyvault "github.com/dapr/components-contrib/crypto/azure/keyvault" + cr_jwks "github.com/dapr/components-contrib/crypto/jwks" + cr_localstorage "github.com/dapr/components-contrib/crypto/localstorage" conf_crypto "github.com/dapr/components-contrib/tests/conformance/cryptography" ) diff --git a/tests/conformance/cryptography/crypto.go b/tests/conformance/cryptography/crypto.go index 377d17e19d..260e9b179d 100644 --- a/tests/conformance/cryptography/crypto.go +++ b/tests/conformance/cryptography/crypto.go @@ -26,7 +26,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - contribCrypto "github.com/dapr/components-contrib/cryptography" + contribCrypto "github.com/dapr/components-contrib/crypto" "github.com/dapr/components-contrib/metadata" "github.com/dapr/components-contrib/tests/conformance/utils" "github.com/dapr/kit/config" From 1f5262cbfb03fc4e2fa90400bcfee09c9a3fabd5 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Fri, 8 Aug 2025 10:56:24 -0500 Subject: [PATCH 20/32] fix: last fixes for gcp pubsub bindingg Signed-off-by: Samantha Coyle --- bindings/gcp/pubsub/metadata.yaml | 95 +----------------------------- bindings/gcp/pubsub/pubsub.go | 79 +++++++++++++++++++++---- bindings/gcp/pubsub/pubsub_test.go | 23 +++++--- pubsub/gcp/pubsub/pubsub.go | 2 +- 4 files changed, 84 insertions(+), 115 deletions(-) diff --git a/bindings/gcp/pubsub/metadata.yaml b/bindings/gcp/pubsub/metadata.yaml index 2bd8db31e0..9927ebe2a1 100644 --- a/bindings/gcp/pubsub/metadata.yaml +++ b/bindings/gcp/pubsub/metadata.yaml @@ -18,58 +18,6 @@ binding: description: "Receive messages from Pub/Sub subscription" builtinAuthenticationProfiles: - name: "gcp" -authenticationProfiles: - - title: "Service Account Authentication" - description: "Authenticate using a GCP service account JSON key." - metadata: - - name: projectID - type: string - required: true - description: The GCP project ID. - example: "my-project" - - name: privateKeyID - type: string - required: true - description: The private key ID from the service account JSON. - example: "abc123def456" - - name: privateKey - type: string - required: true - description: The private key from the service account JSON. - example: | - -----BEGIN PRIVATE KEY----- - XXX - -----END PRIVATE KEY----- - - name: clientEmail - type: string - required: true - description: The client email from the service account JSON. - example: "pubsub@my-project.iam.gserviceaccount.com" - - name: clientID - type: string - required: true - description: The client ID from the service account JSON. - example: "123456789012345678901" - - name: authURI - type: string - required: true - description: The authentication URI from the service account JSON. - example: "https://accounts.google.com/o/oauth2/auth" - - name: tokenURI - type: string - required: true - description: The token URI from the service account JSON. - example: "https://oauth2.googleapis.com/token" - - name: authProviderX509CertURL - type: string - required: true - description: The auth provider certificate URL from the service account JSON. - example: "https://www.googleapis.com/oauth2/v1/certs" - - name: clientX509CertURL - type: string - required: true - description: The client certificate URL from the service account JSON. - example: "https://www.googleapis.com/robot/v1/metadata/x509/pubsub%40my-project.iam.gserviceaccount.com" metadata: - name: topic required: true @@ -78,45 +26,4 @@ metadata: - name: subscription required: false description: "The Pub/Sub subscription name" - example: '"my-subscription"' - - name: Type - required: false - description: "The type of service account" - example: '"service_account"' - - name: PrivateKey - required: false - description: "The private key for service account authentication" - example: '"-----BEGIN PRIVATE KEY-----\nXXX..."' - sensitive: true - - name: TokenURI - required: false - description: "The token URI for authentication" - example: '"https://oauth2.googleapis.com/token"' - - name: AuthProviderX509CertURL - required: false - description: "The auth provider X509 certificate URL" - example: '"https://www.googleapis.com/oauth2/v1/certs"' - - name: ProjectID - required: false - description: "The GCP project ID" - example: '"my-project-id"' - - name: PrivateKeyID - required: false - description: "The private key ID" - example: '"key-id"' - - name: ClientEmail - required: false - description: "The client email for service account" - example: '"service-account@my-project.iam.gserviceaccount.com"' - - name: ClientID - required: false - description: "The client ID" - example: '"client-id"' - - name: AuthURI - required: false - description: "The auth URI" - example: '"https://accounts.google.com/o/oauth2/auth"' - - name: ClientX509CertURL - required: false - description: "The client X509 certificate URL" - example: '"https://www.googleapis.com/robot/v1/metadata/x509/service-account%40my-project.iam.gserviceaccount.com"' \ No newline at end of file + example: '"my-subscription"' \ No newline at end of file diff --git a/bindings/gcp/pubsub/pubsub.go b/bindings/gcp/pubsub/pubsub.go index 7e9c849d40..0fe977df50 100644 --- a/bindings/gcp/pubsub/pubsub.go +++ b/bindings/gcp/pubsub/pubsub.go @@ -28,6 +28,7 @@ import ( "github.com/dapr/components-contrib/bindings" contribMetadata "github.com/dapr/components-contrib/metadata" "github.com/dapr/kit/logger" + kitmd "github.com/dapr/kit/metadata" ) const ( @@ -64,6 +65,20 @@ type pubSubMetadata struct { ClientX509CertURL string `json:"clientX509CertURL"` } +// TODO: in future, we need to clean this up to rm duplication between this and the pubsub gcp pubsub component +type GCPAuthJSON struct { + ProjectID string `json:"project_id"` + PrivateKeyID string `json:"private_key_id"` + PrivateKey string `json:"private_key"` + ClientEmail string `json:"client_email"` + ClientID string `json:"client_id"` + AuthURI string `json:"auth_uri"` + TokenURI string `json:"token_uri"` + AuthProviderCertURL string `json:"auth_provider_x509_cert_url"` + ClientCertURL string `json:"client_x509_cert_url"` + Type string `json:"type"` +} + // NewGCPPubSub returns a new GCPPubSub instance. func NewGCPPubSub(logger logger.Logger) bindings.InputOutputBinding { return &GCPPubSub{ @@ -72,32 +87,74 @@ func NewGCPPubSub(logger logger.Logger) bindings.InputOutputBinding { } } -// Init parses metadata and creates a new Pub Sub client. -func (g *GCPPubSub) Init(ctx context.Context, metadata bindings.Metadata) error { - b, err := g.parseMetadata(metadata) +func parseMetadata(metadata bindings.Metadata) (*pubSubMetadata, error) { + result := pubSubMetadata{ + Type: "service_account", + } + + err := kitmd.DecodeMetadata(metadata.Properties, &result) if err != nil { - return err + return nil, err + } + + if result.ProjectID == "" { + return nil, fmt.Errorf("missing attribute projectID") } - var pubsubMeta pubSubMetadata - err = json.Unmarshal(b, &pubsubMeta) + return &result, nil +} + +// Init parses metadata and creates a new Pub Sub client. +func (g *GCPPubSub) Init(ctx context.Context, metadata bindings.Metadata) error { + pubsubMeta, err := parseMetadata(metadata) if err != nil { return err } - clientOptions := option.WithCredentialsJSON(b) - pubsubClient, err := pubsub.NewClient(ctx, pubsubMeta.ProjectID, clientOptions) + + pubsubClient, err := g.getPubSubClient(ctx, pubsubMeta) if err != nil { return fmt.Errorf("error creating pubsub client: %s", err) } g.client = pubsubClient - g.metadata = &pubsubMeta + g.metadata = pubsubMeta return nil } -func (g *GCPPubSub) parseMetadata(metadata bindings.Metadata) ([]byte, error) { - return json.Marshal(metadata.Properties) +func (g *GCPPubSub) getPubSubClient(_ context.Context, metadata *pubSubMetadata) (*pubsub.Client, error) { + var pubsubClient *pubsub.Client + var err error + + if metadata.PrivateKeyID != "" { + // TODO: validate that all auth json fields are filled + authJSON := &GCPAuthJSON{ + ProjectID: metadata.ProjectID, + PrivateKeyID: metadata.PrivateKeyID, + PrivateKey: metadata.PrivateKey, + ClientEmail: metadata.ClientEmail, + ClientID: metadata.ClientID, + AuthURI: metadata.AuthURI, + TokenURI: metadata.TokenURI, + AuthProviderCertURL: metadata.AuthProviderX509CertURL, + ClientCertURL: metadata.ClientX509CertURL, + Type: metadata.Type, + } + gcpCompatibleJSON, _ := json.Marshal(authJSON) + clientOptions := option.WithCredentialsJSON(gcpCompatibleJSON) + pubsubClient, err = pubsub.NewClient(context.Background(), metadata.ProjectID, clientOptions) + if err != nil { + return pubsubClient, err + } + } else { + // Use implicit credentials + pubsubClient, err = pubsub.NewClient(context.Background(), metadata.ProjectID) + if err != nil { + return pubsubClient, err + } + } + + return pubsubClient, nil } func (g *GCPPubSub) Read(ctx context.Context, handler bindings.Handler) error { diff --git a/bindings/gcp/pubsub/pubsub_test.go b/bindings/gcp/pubsub/pubsub_test.go index 5d0afb436a..592f8dedc9 100644 --- a/bindings/gcp/pubsub/pubsub_test.go +++ b/bindings/gcp/pubsub/pubsub_test.go @@ -14,28 +14,33 @@ limitations under the License. package pubsub import ( - "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/dapr/components-contrib/bindings" - "github.com/dapr/kit/logger" ) func TestInit(t *testing.T) { m := bindings.Metadata{} m.Properties = map[string]string{ - "auth_provider_x509_cert_url": "https://auth", "auth_uri": "https://auth", "client_x509_cert_url": "https://cert", "client_email": "test@test.com", "client_id": "id", "private_key": "****", - "private_key_id": "key_id", "project_id": "project1", "token_uri": "https://token", "type": "serviceaccount", "topic": "t1", "subscription": "s1", + "authProviderX509CertURL": "https://auth", + "authURI": "https://auth", + "clientX509CertURL": "https://cert", + "clientEmail": "test@test.com", + "clientID": "id", + "privateKey": "****", + "privateKeyID": "key_id", + "projectID": "project1", + "tokenURI": "https://token", + "type": "serviceaccount", + "topic": "t1", + "subscription": "s1", } - ps := GCPPubSub{logger: logger.NewLogger("test")} - b, err := ps.parseMetadata(m) - require.NoError(t, err) - var pubsubMeta pubSubMetadata - err = json.Unmarshal(b, &pubsubMeta) + // Test metadata parsing only + pubsubMeta, err := parseMetadata(m) require.NoError(t, err) assert.Equal(t, "s1", pubsubMeta.Subscription) diff --git a/pubsub/gcp/pubsub/pubsub.go b/pubsub/gcp/pubsub/pubsub.go index d7e63627ec..7cf99cb694 100644 --- a/pubsub/gcp/pubsub/pubsub.go +++ b/pubsub/gcp/pubsub/pubsub.go @@ -170,7 +170,7 @@ func (g *GCPPubSub) Init(ctx context.Context, meta pubsub.Metadata) error { return nil } -func (g *GCPPubSub) getPubSubClient(ctx context.Context, metadata *metadata) (*gcppubsub.Client, error) { +func (g *GCPPubSub) getPubSubClient(_ context.Context, metadata *metadata) (*gcppubsub.Client, error) { var pubsubClient *gcppubsub.Client var err error From 94cb68102426ce90f6cc968512abd28cc2220390 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Fri, 8 Aug 2025 11:10:16 -0500 Subject: [PATCH 21/32] style: fix for metadata analyzer Signed-off-by: Samantha Coyle --- bindings/gcp/pubsub/pubsub.go | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/bindings/gcp/pubsub/pubsub.go b/bindings/gcp/pubsub/pubsub.go index 0fe977df50..5287f5af9d 100644 --- a/bindings/gcp/pubsub/pubsub.go +++ b/bindings/gcp/pubsub/pubsub.go @@ -51,18 +51,20 @@ type GCPPubSub struct { // TODO: in future, this needs to use the same setup that pubsub gcp pubsub component uses, // so we can embed the builtin auth profile instead... type pubSubMetadata struct { - Topic string `json:"topic"` - Subscription string `json:"subscription"` - Type string `json:"type"` - ProjectID string `json:"projectID"` - PrivateKeyID string `json:"privateKeyID"` - PrivateKey string `json:"privateKey"` - ClientEmail string `json:"clientEmail"` - ClientID string `json:"clientID"` - AuthURI string `json:"authURI"` - TokenURI string `json:"tokenURI"` - AuthProviderX509CertURL string `json:"authProviderX509CertURL"` - ClientX509CertURL string `json:"clientX509CertURL"` + Topic string `json:"topic"` + Subscription string `json:"subscription"` + + // metadata analyzer needs to ignore these fields as they are part of the builtin auth profile + Type string `json:"type" mdignore:"true"` + ProjectID string `json:"projectID" mdignore:"true"` + PrivateKeyID string `json:"privateKeyID" mdignore:"true"` + PrivateKey string `json:"privateKey" mdignore:"true"` + ClientEmail string `json:"clientEmail" mdignore:"true"` + ClientID string `json:"clientID" mdignore:"true"` + AuthURI string `json:"authURI" mdignore:"true"` + TokenURI string `json:"tokenURI" mdignore:"true"` + AuthProviderX509CertURL string `json:"authProviderX509CertURL" mdignore:"true"` + ClientX509CertURL string `json:"clientX509CertURL" mdignore:"true"` } // TODO: in future, we need to clean this up to rm duplication between this and the pubsub gcp pubsub component From 559f12a288456b5b57983a597bed695c25c1033c Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Fri, 8 Aug 2025 11:38:24 -0500 Subject: [PATCH 22/32] style: appease linter Signed-off-by: Samantha Coyle --- bindings/gcp/pubsub/pubsub.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/gcp/pubsub/pubsub.go b/bindings/gcp/pubsub/pubsub.go index 5287f5af9d..596cf8f03e 100644 --- a/bindings/gcp/pubsub/pubsub.go +++ b/bindings/gcp/pubsub/pubsub.go @@ -100,7 +100,7 @@ func parseMetadata(metadata bindings.Metadata) (*pubSubMetadata, error) { } if result.ProjectID == "" { - return nil, fmt.Errorf("missing attribute projectID") + return nil, errors.New("missing attribute projectID") } return &result, nil From ef4a474b128198f8e41acf0772442426cd3dcf05 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Mon, 11 Aug 2025 09:57:44 -0500 Subject: [PATCH 23/32] revert: checkout state dir from main since in diff pr now Signed-off-by: Samantha Coyle --- state/aerospike/aerospike.go | 6 +- state/aerospike/metadata.yaml | 26 --- state/alicloud/tablestore/metadata.yaml | 36 ----- state/alicloud/tablestore/mock_client.go | 12 -- state/couchbase/couchbase.go | 12 +- state/couchbase/metadata.yaml | 43 ----- state/etcd/etcd.go | 2 +- state/etcd/metadata.yaml | 69 -------- state/gcp/firestore/firestore.go | 5 +- state/gcp/firestore/metadata.yaml | 99 ------------ state/hashicorp/consul/metadata.yaml | 36 ----- state/hazelcast/hazelcast.go | 4 +- state/hazelcast/metadata.yaml | 21 --- state/jetstream/metadata.yaml | 37 ----- state/oci/objectstorage/metadata.yaml | 84 ---------- state/oci/objectstorage/objectstorage.go | 3 +- state/oracledatabase/metadata.yaml | 31 ---- state/oracledatabase/oracledatabaseaccess.go | 22 +-- state/redis/metadata.yaml | 26 +-- state/rethinkdb/metadata.yaml | 152 ------------------ state/rethinkdb/rethinkdb.go | 98 +---------- state/sqlite/metadata.yaml | 53 ------ state/sqlserver/sqlserver.go | 34 ++-- state/sqlserver/sqlserver_integration_test.go | 30 +--- state/zookeeper/metadata.yaml | 39 ----- state/zookeeper/zk.go | 1 - 26 files changed, 41 insertions(+), 940 deletions(-) delete mode 100644 state/aerospike/metadata.yaml delete mode 100644 state/alicloud/tablestore/metadata.yaml delete mode 100644 state/couchbase/metadata.yaml delete mode 100644 state/etcd/metadata.yaml delete mode 100644 state/gcp/firestore/metadata.yaml delete mode 100644 state/hashicorp/consul/metadata.yaml delete mode 100644 state/hazelcast/metadata.yaml delete mode 100644 state/jetstream/metadata.yaml delete mode 100644 state/oci/objectstorage/metadata.yaml delete mode 100644 state/oracledatabase/metadata.yaml delete mode 100644 state/rethinkdb/metadata.yaml delete mode 100644 state/sqlite/metadata.yaml delete mode 100644 state/zookeeper/metadata.yaml diff --git a/state/aerospike/aerospike.go b/state/aerospike/aerospike.go index ea95c1516d..27206519b6 100644 --- a/state/aerospike/aerospike.go +++ b/state/aerospike/aerospike.go @@ -34,9 +34,9 @@ import ( ) type aerospikeMetadata struct { - Hosts string `json:"hosts"` - Namespace string `json:"namespace"` - Set string `json:"set"` // optional + Hosts string + Namespace string + Set string // optional } var ( diff --git a/state/aerospike/metadata.yaml b/state/aerospike/metadata.yaml deleted file mode 100644 index 379cbf74f1..0000000000 --- a/state/aerospike/metadata.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: state -name: aerospike -version: v1 -status: alpha -title: "Aerospike" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-aerospike/ -metadata: - - name: hosts - type: string - required: true - description: Comma-separated list of Aerospike hosts. - example: "localhost:3000,aerospike:3000" - - name: namespace - type: string - required: true - description: The Aerospike namespace. - example: "test" - - name: set - type: string - required: false - description: The Aerospike set name. - example: "dapr-state" \ No newline at end of file diff --git a/state/alicloud/tablestore/metadata.yaml b/state/alicloud/tablestore/metadata.yaml deleted file mode 100644 index cf5f1215cc..0000000000 --- a/state/alicloud/tablestore/metadata.yaml +++ /dev/null @@ -1,36 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: state -name: alicloud.tablestore -version: v1 -status: alpha -title: "AliCloud TableStore" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-state-stores/ -metadata: - - name: endpoint - type: string - required: true - description: The endpoint of the AliCloud TableStore instance. - example: "https://tablestore.aliyuncs.com" - - name: accessKeyID - type: string - required: true - description: The access key ID of the AliCloud TableStore instance. - example: "my_access_key_id" - - name: accessKey - type: string - required: true - description: The access key of the AliCloud TableStore instance. - example: "my_access_key" - - name: instanceName - type: string - required: true - description: The name of the AliCloud TableStore instance. - example: "my_instance" - - name: tableName - type: string - required: true - description: The name of the table to use. - example: "my_table" \ No newline at end of file diff --git a/state/alicloud/tablestore/mock_client.go b/state/alicloud/tablestore/mock_client.go index 4718c2d830..f29678525f 100644 --- a/state/alicloud/tablestore/mock_client.go +++ b/state/alicloud/tablestore/mock_client.go @@ -16,7 +16,6 @@ package tablestore import ( "bytes" "encoding/binary" - "sync" "github.com/aliyun/aliyun-tablestore-go-sdk/tablestore" ) @@ -25,7 +24,6 @@ type mockClient struct { tablestore.TableStoreClient data map[string][]byte - mu sync.RWMutex } func (m *mockClient) DeleteRow(request *tablestore.DeleteRowRequest) (*tablestore.DeleteRowResponse, error) { @@ -38,9 +36,7 @@ func (m *mockClient) DeleteRow(request *tablestore.DeleteRowRequest) (*tablestor } } - m.mu.Lock() delete(m.data, key) - m.mu.Unlock() return nil, nil } @@ -55,9 +51,7 @@ func (m *mockClient) GetRow(request *tablestore.GetRowRequest) (*tablestore.GetR } } - m.mu.RLock() val := m.data[key] - m.mu.RUnlock() resp := &tablestore.GetRowResponse{ Columns: []*tablestore.AttributeColumn{{ @@ -93,9 +87,7 @@ func (m *mockClient) UpdateRow(req *tablestore.UpdateRowRequest) (*tablestore.Up } } - m.mu.Lock() m.data[key] = val - m.mu.Unlock() return nil, nil } @@ -105,7 +97,6 @@ func (m *mockClient) BatchGetRow(request *tablestore.BatchGetRowRequest) (*table TableToRowsResult: map[string][]tablestore.RowResult{}, } - m.mu.RLock() for _, criteria := range request.MultiRowQueryCriteria { tableRes := resp.TableToRowsResult[criteria.TableName] if tableRes == nil { @@ -145,14 +136,12 @@ func (m *mockClient) BatchGetRow(request *tablestore.BatchGetRowRequest) (*table } } } - m.mu.RUnlock() return resp, nil } func (m *mockClient) BatchWriteRow(request *tablestore.BatchWriteRowRequest) (*tablestore.BatchWriteRowResponse, error) { resp := &tablestore.BatchWriteRowResponse{} - m.mu.Lock() for _, changes := range request.RowChangesGroupByTable { for _, change := range changes { switch inst := change.(type) { @@ -185,7 +174,6 @@ func (m *mockClient) BatchWriteRow(request *tablestore.BatchWriteRowRequest) (*t } } } - m.mu.Unlock() return resp, nil } diff --git a/state/couchbase/couchbase.go b/state/couchbase/couchbase.go index 562f748286..32264c78ef 100644 --- a/state/couchbase/couchbase.go +++ b/state/couchbase/couchbase.go @@ -57,12 +57,12 @@ type Couchbase struct { } type couchbaseMetadata struct { - CouchbaseURL string `json:"couchbaseURL"` - Username string `json:"username"` - Password string `json:"password"` - BucketName string `json:"bucketName"` - NumReplicasDurableReplication uint `json:"numReplicasDurableReplication"` - NumReplicasDurablePersistence uint `json:"numReplicasDurablePersistence"` + CouchbaseURL string + Username string + Password string + BucketName string + NumReplicasDurableReplication uint + NumReplicasDurablePersistence uint } // NewCouchbaseStateStore returns a new couchbase state store. diff --git a/state/couchbase/metadata.yaml b/state/couchbase/metadata.yaml deleted file mode 100644 index e1269e2ed3..0000000000 --- a/state/couchbase/metadata.yaml +++ /dev/null @@ -1,43 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: state -name: couchbase -version: v1 -status: alpha -title: "Couchbase" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-couchbase/ -authenticationProfiles: - - title: "Connection String" - description: "Connect to Couchbase using a connection string." - metadata: - - name: couchbaseURL - type: string - required: true - description: The Couchbase connection string. - example: "couchbase://localhost" - - name: username - type: string - required: true - - name: password - type: string - required: true - - name: bucketName - type: string - required: true - description: The Couchbase bucket name. - example: "default" -metadata: - - name: numReplicasDurableReplication - type: number - required: false - description: The number of replicas for durable replication. - example: 1 - default: 0 - - name: numReplicasDurablePersistence - type: number - required: false - description: The number of replicas for durable persistence. - example: 1 - default: 0 \ No newline at end of file diff --git a/state/etcd/etcd.go b/state/etcd/etcd.go index b7a1db3a69..67272071ee 100644 --- a/state/etcd/etcd.go +++ b/state/etcd/etcd.go @@ -123,7 +123,7 @@ func (e *Etcd) ParseClientFromConfig(etcdConfig *etcdConfig) (*clientv3.Client, config := clientv3.Config{ Endpoints: endpoints, - DialTimeout: 5 * time.Second, // TODO: make this configurable + DialTimeout: 5 * time.Second, TLS: tlsConfig, } client, err := clientv3.New(config) diff --git a/state/etcd/metadata.yaml b/state/etcd/metadata.yaml deleted file mode 100644 index e357b8785a..0000000000 --- a/state/etcd/metadata.yaml +++ /dev/null @@ -1,69 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: state -name: etcd -version: v1 -status: alpha -title: "etcd" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-etcd/ -authenticationProfiles: - - title: "Basic Authentication" - description: "Connect to etcd without TLS encryption." - metadata: - - name: endpoints - type: string - required: true - description: Comma-separated list of etcd endpoints. - example: "localhost:2379" - - title: "TLS Authentication" - description: "Connect to etcd using TLS encryption with certificates." - metadata: - - name: endpoints - type: string - required: true - description: Comma-separated list of etcd endpoints. - example: "localhost:2379" - - name: tlsEnable - type: string - required: true - description: Enable TLS authentication. - example: "true" - - name: ca - type: string - required: true - description: CA certificate for TLS verification. - example: | - -----BEGIN CERTIFICATE----- - XXX - -----END CERTIFICATE----- - - name: cert - type: string - required: true - description: Client certificate for TLS authentication. - example: | - -----BEGIN CERTIFICATE----- - XXX - -----END CERTIFICATE----- - - name: key - type: string - required: true - description: Client private key for TLS authentication. - example: | - -----BEGIN PRIVATE KEY----- - XXX - -----END PRIVATE KEY----- -metadata: - - name: keyPrefixPath - type: string - required: false - description: The key prefix path to use. - example: "my_key_prefix_path" - default: "" - - name: maxTxnOps - type: number - required: false - description: Maximum number of operations allowed in a transaction. - example: 128 - default: 128 \ No newline at end of file diff --git a/state/gcp/firestore/firestore.go b/state/gcp/firestore/firestore.go index 6157c6646f..2e4b8eb62e 100644 --- a/state/gcp/firestore/firestore.go +++ b/state/gcp/firestore/firestore.go @@ -47,9 +47,6 @@ type Firestore struct { } type firestoreMetadata struct { - // TODO: update these to use camel case instead in future - - // TODO: rm type field? It's stable component but this field is NOT used anywhere except in tests. Type string `json:"type"` ProjectID string `json:"project_id" mapstructure:"project_id"` PrivateKeyID string `json:"private_key_id" mapstructure:"private_key_id"` @@ -222,7 +219,7 @@ func (f *Firestore) Close() error { return nil } -func getGCPClient(_ context.Context, metadata *firestoreMetadata, l logger.Logger) (*datastore.Client, error) { +func getGCPClient(ctx context.Context, metadata *firestoreMetadata, l logger.Logger) (*datastore.Client, error) { var gcpClient *datastore.Client var err error diff --git a/state/gcp/firestore/metadata.yaml b/state/gcp/firestore/metadata.yaml deleted file mode 100644 index ce7aed3851..0000000000 --- a/state/gcp/firestore/metadata.yaml +++ /dev/null @@ -1,99 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: state -name: gcp.firestore -version: v1 -status: stable -title: "GCP Firestore" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-firestore/ -authenticationProfiles: - - title: "Service Account Authentication" - description: "Authenticate using a GCP service account JSON key." - metadata: - - name: project_id - type: string - required: true - description: The GCP project ID. - example: "my-project" - - name: private_key_id - type: string - required: true - description: The private key ID from the service account JSON. - example: "abc123def456" - - name: private_key - type: string - required: true - description: The private key from the service account JSON. - example: | - -----BEGIN PRIVATE KEY----- - MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC6VJUIbJjbCWsN - # ... (truncated) ... - -----END PRIVATE KEY----- - - name: client_email - type: string - required: true - description: The client email from the service account JSON. - example: "firestore@my-project.iam.gserviceaccount.com" - - name: client_id - type: string - required: true - description: The client ID from the service account JSON. - example: "123456789012345678901" - - name: auth_uri - type: string - required: true - description: The authentication URI from the service account JSON. - example: "https://accounts.google.com/o/oauth2/auth" - - name: token_uri - type: string - required: true - description: The token URI from the service account JSON. - example: "https://oauth2.googleapis.com/token" - - name: auth_provider_x509_cert_url - type: string - required: true - description: The auth provider certificate URL from the service account JSON. - example: "https://www.googleapis.com/oauth2/v1/certs" - - name: client_x509_cert_url - type: string - required: true - description: The client certificate URL from the service account JSON. - example: "https://www.googleapis.com/robot/v1/metadata/x509/firestore%40my-project.iam.gserviceaccount.com" - - title: "Implicit Credentials" - description: "Authenticate using implicit credentials (Application Default Credentials) for local development." - metadata: - - name: project_id - type: string - required: true - description: The GCP project ID. - example: "my-project" - - name: endpoint - type: string - required: false - description: The Firestore endpoint URL (for custom endpoints or emulator). - example: "https://firestore.googleapis.com" -metadata: - - name: entity_kind - type: string - required: false - description: The entity kind (collection name) for storing state data. - example: "dapr-state" - default: "DaprState" - - name: type - type: string - required: false - description: The type of service account. - example: "service_account" - - name: connectionEndpoint - type: string - required: false - description: The Firestore connection endpoint. - example: "https://firestore.googleapis.com" - - name: noIndex - type: bool - required: false - description: Whether to disable indexing for the entity. - example: false - default: false \ No newline at end of file diff --git a/state/hashicorp/consul/metadata.yaml b/state/hashicorp/consul/metadata.yaml deleted file mode 100644 index 82c3ce0ce1..0000000000 --- a/state/hashicorp/consul/metadata.yaml +++ /dev/null @@ -1,36 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: state -name: hashicorp.consul -version: v1 -status: alpha -title: "Echo" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-consul/ -metadata: - - name: datacenter - type: string - required: true - description: The datacenter to use. - example: "dc1" - - name: httpAddr - type: string - required: true - description: The HTTP address of the Consul server. - example: "http://localhost:8500" - - name: aclToken - type: string - required: true - description: The ACL token to use. - example: "my_acl_token" - - name: scheme - type: string - required: true - description: The scheme to use. - example: "http" - - name: keyPrefixPath - type: string - required: true - description: The key prefix path to use. - example: "my_key_prefix_path" \ No newline at end of file diff --git a/state/hazelcast/hazelcast.go b/state/hazelcast/hazelcast.go index 9ef11e08a9..2fe56f0488 100644 --- a/state/hazelcast/hazelcast.go +++ b/state/hazelcast/hazelcast.go @@ -40,8 +40,8 @@ type Hazelcast struct { } type hazelcastMetadata struct { - HazelcastServers string `json:"hazelcastServers" mapstructure:"hazelcastServers"` - HazelcastMap string `json:"hazelcastMap" mapstructure:"hazelcastMap"` + HazelcastServers string + HazelcastMap string } // NewHazelcastStore returns a new hazelcast backed state store. diff --git a/state/hazelcast/metadata.yaml b/state/hazelcast/metadata.yaml deleted file mode 100644 index a8bd2822c0..0000000000 --- a/state/hazelcast/metadata.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: state -name: hazelcast -version: v1 -status: alpha -title: "Hazelcast" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-hazelcast/ -metadata: - - name: hazelcastServers - type: string - required: true - description: Comma-separated list of Hazelcast servers. - example: "localhost:5701" - - name: hazelcastMap - type: string - required: true - description: The Hazelcast map name. - example: "dapr-state" \ No newline at end of file diff --git a/state/jetstream/metadata.yaml b/state/jetstream/metadata.yaml deleted file mode 100644 index 509ea12f51..0000000000 --- a/state/jetstream/metadata.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: state -name: jetstream -version: v1 -status: alpha -title: "JetStream KV" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-jetstream-kv/ -metadata: - - name: name - type: string - required: false - description: The name of the bucket to use. - example: "my_bucket" - default: "dapr.io - statestore.jetstream" - - name: natsURL - type: string - required: true - description: The NATS URL to use. - example: "nats://localhost:4222" - - name: jwt - type: string - required: false - description: The JWT to use. - example: "my_jwt" - - name: seedKey - type: string - required: false - description: The seed key to use. - example: "my_seed_key" - - name: bucket - type: string - required: true - description: The bucket to use. - example: "my_bucket" \ No newline at end of file diff --git a/state/oci/objectstorage/metadata.yaml b/state/oci/objectstorage/metadata.yaml deleted file mode 100644 index 4c43af36b4..0000000000 --- a/state/oci/objectstorage/metadata.yaml +++ /dev/null @@ -1,84 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: state -name: oci.objectstorage -version: v1 -status: alpha -title: "OCI Object Storage" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-oci-objectstorage/ -authenticationProfiles: - - title: "API Key Authentication" - description: "Authenticate using OCI API key with user OCID, fingerprint, and private key." - metadata: - - name: region - type: string - required: true - description: The OCI region where the bucket is located. - example: "us-ashburn-1" - - name: userOCID - type: string - required: true - description: The OCID of the user. - example: "ocid1.user.oc1..example" - - name: fingerPrint - type: string - required: true - description: The fingerprint of the API key. - example: "xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx" - - name: privateKey - type: string - required: true - description: The private key for API authentication. - example: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" - - name: tenancyOCID - type: string - required: true - description: The OCID of the tenancy. - example: "ocid1.tenancy.oc1..example" - - title: "Instance Principal Authentication" - description: "Authenticate using OCI instance principal (automatically available on OCI compute instances)." - metadata: - - name: instancePrincipalAuthentication - type: bool - required: true - description: Set to true to use instance principal authentication. - example: true - default: false - - title: "Configuration File Authentication" - description: "Authenticate using OCI configuration file." - metadata: - - name: configFileAuthentication - type: bool - required: true - description: Set to true to use configuration file authentication. - example: true - default: falsee - - name: configFilePath - type: string - required: true - description: Path to the OCI configuration file. - example: "~/.oci/config" - - name: configFileProfile - type: string - required: false - description: Profile name in the OCI configuration file. - example: "DEFAULT" - default: "" -metadata: - - name: bucketName - type: string - required: true - description: The name of the OCI Object Storage bucket. - example: "my-bucket" - - name: compartmentOCID - type: string - required: true - description: The OCID of the compartment containing the bucket. - example: "ocid1.compartment.oc1..example" - - name: namespace - type: string - required: false - description: The OCI namespace for the object storage. - example: "my-namespace" \ No newline at end of file diff --git a/state/oci/objectstorage/objectstorage.go b/state/oci/objectstorage/objectstorage.go index 147ef472c9..d2d87accb1 100644 --- a/state/oci/objectstorage/objectstorage.go +++ b/state/oci/objectstorage/objectstorage.go @@ -51,6 +51,7 @@ const ( privateKeyKey = "privateKey" userKey = "userOCID" bucketNameKey = "bucketName" + metadataTTLKey = "ttlInSeconds" daprStateStoreMetaLabel = "dapr-state-store" expiryTimeMetaLabel = "expiry-time-from-ttl" isoDateTimeFormat = "2006-01-02T15:04:05" @@ -79,7 +80,7 @@ type objectStoreMetadata struct { InstancePrincipalAuthentication bool ConfigFileAuthentication bool - OCIObjectStorageClient *objectstorage.ObjectStorageClient `mapstructure:"-"` + OCIObjectStorageClient *objectstorage.ObjectStorageClient } type objectStoreClient interface { diff --git a/state/oracledatabase/metadata.yaml b/state/oracledatabase/metadata.yaml deleted file mode 100644 index ca46065bad..0000000000 --- a/state/oracledatabase/metadata.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: state -name: oracledatabase -version: v1 -status: alpha -title: "Oracle Database" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-oracledatabase/ -authenticationProfiles: - - title: "Connection String" - description: "Connect to Oracle Database using a connection string." - metadata: - - name: connectionString - type: string - required: true -metadata: - - name: tableName - type: string - required: false - description: The table name to store state data. - example: "dapr_state" - default: "state" - - name: oracleWalletLocation - type: string - required: false - description: The location of the Oracle wallet. - example: "/path/to/wallet" - default: "" - \ No newline at end of file diff --git a/state/oracledatabase/oracledatabaseaccess.go b/state/oracledatabase/oracledatabaseaccess.go index 42a9c77aa6..9a62ce6d4d 100644 --- a/state/oracledatabase/oracledatabaseaccess.go +++ b/state/oracledatabase/oracledatabaseaccess.go @@ -50,9 +50,9 @@ type oracleDatabaseAccess struct { } type oracleDatabaseMetadata struct { - ConnectionString string `json:"connectionString"` - OracleWalletLocation string `json:"oracleWalletLocation"` - TableName string `json:"tableName"` + ConnectionString string + OracleWalletLocation string + TableName string } // newOracleDatabaseAccess creates a new instance of oracleDatabaseAccess. @@ -514,16 +514,8 @@ func (o *oracleDatabaseAccess) ensureStateTable(stateTableName string) error { } func tableExists(db *sql.DB, tableName string) (bool, error) { - //nolint:gosec - query := fmt.Sprintf("SELECT 1 FROM %s WHERE ROWNUM = 1", tableName) - - var dummy int - err := db.QueryRow(query).Scan(&dummy) - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return true, nil // Table exists but is empty - } - return false, nil // Likely a table does not exist error - } - return true, nil + var tblCount int32 + err := db.QueryRow("SELECT count(table_name) tbl_count FROM user_tables WHERE table_name = upper(:tablename)", tableName).Scan(&tblCount) + exists := tblCount > 0 + return exists, err } diff --git a/state/redis/metadata.yaml b/state/redis/metadata.yaml index cab536678f..79898a759b 100644 --- a/state/redis/metadata.yaml +++ b/state/redis/metadata.yaml @@ -36,30 +36,6 @@ authenticationProfiles: secret reference example: "KeFg23!" default: "" - - name: sentinelUsername - type: string - required: false - description: | - Username for Redis Sentinel. Applicable only when "failover" is true, and - Redis Sentinel has authentication enabled. Defaults to empty. - example: "my-sentinel-username" - default: "" - url: - title: "Redis Sentinel authentication documentation" - url: "https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/#configuring-sentinel-instances-with-authentication" - - name: sentinelPassword - type: string - required: false - sensitive: true - description: | - Password for Redis Sentinel. Applicable only when "failover" is true, and - Redis Sentinel has authentication enabled. Use secretKeyRef for - secret reference. Defaults to empty. - example: "KeFg23!" - default: "" - url: - title: "Redis Sentinel authentication documentation" - url: "https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/#configuring-sentinel-instances-with-authentication" metadata: - name: redisHost required: true @@ -96,7 +72,7 @@ metadata: required: false description: | The Redis sentinel master name. Required when "failover" is enabled. - example: "mymaster" + example: "127.0.0.1:6379" type: string url: title: "Redis Sentinel documentation" diff --git a/state/rethinkdb/metadata.yaml b/state/rethinkdb/metadata.yaml deleted file mode 100644 index 26edf5c0c2..0000000000 --- a/state/rethinkdb/metadata.yaml +++ /dev/null @@ -1,152 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: state -name: rethinkdb -version: v1 -status: beta -title: "RethinkDB" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-rethinkdb/ -authenticationProfiles: - - title: "Basic Authentication" - description: "Authenticate using username and password." - metadata: - - name: address - type: string - required: false - description: The RethinkDB server address. - example: "localhost:28015" - - name: addresses - type: string - required: false - description: Comma-separated list of RethinkDB server addresses. - example: "localhost:28015,localhost:28016" - - name: database - type: string - required: true - description: The RethinkDB database name. - example: "dapr" - default: "" - - name: username - type: string - required: false - description: The username for authentication. If not provided, the admin user is used for v1 handshake protocol. - example: "admin" - - name: password - type: string - required: false - description: The password for authentication. This is only used for v1 handshake protocol. - example: "password" - - title: "TLS Authentication" - description: "Authenticate using client certificate and key." - metadata: - - name: enableTLS - type: bool - required: false - description: Whether to enable TLS encryption. - example: false - default: false - - name: clientCert - type: string - required: true - description: The client certificate for TLS authentication. - example: "-----BEGIN CERTIFICATE-----\nXXX..." - - name: clientKey - type: string - required: true - description: The client key for TLS authentication. - example: "-----BEGIN PRIVATE KEY-----\nXXX..." - sensitive: true -metadata: - - name: table - type: string - required: false - description: The table name to store state data. - example: "daprstate" - default: "daprstate" - - name: archive - type: bool - required: false - description: Whether to archive changes to a separate table. - example: false - default: false - - name: timeout - type: string - required: false - description: Connection timeout duration. - example: "10s" - - name: useJSONNumber - type: bool - required: false - description: Whether to use json.Number instead of float64. - example: false - default: false - - name: numRetries - type: number - required: false - description: Number of times to retry queries on connection errors. - example: 3 - - name: hostDecayDuration - type: string - required: false - description: Host decay duration for weighted host selection. - example: "5m" - default: "5m" - - name: useOpentracing - type: bool - required: false - description: Whether to enable opentracing for queries. - example: false - default: false - - name: writeTimeout - type: string - required: false - description: Write timeout duration." - example: "10s" - - name: readTimeout - type: string - required: false - description: Read timeout duration." - example: "10s" - - name: handshakeVersion - type: number - required: false - description: Handshake version for RethinkDB." - example: 1 - - name: keepAlivePeriod - type: string - required: false - description: Keep alive period duration." - example: "30s" - - name: maxIdle - type: number - required: false - description: Maximum number of idle connections." - example: 5 - - name: authKey - type: string - required: false - description: The authentication key for RethinkDB." - example: "auth-key" - sensitive: true - - name: initialCap - type: number - required: false - description: Initial connection pool capacity." - example: 5 - - name: maxOpen - type: number - required: false - description: Maximum number of open connections." - example: 10 - - name: discoverHosts - type: bool - required: false - description: Whether to discover hosts." - example: false - - name: nodeRefreshInterval - type: string - required: false - description: Node refresh interval duration." - example: "5m" \ No newline at end of file diff --git a/state/rethinkdb/rethinkdb.go b/state/rethinkdb/rethinkdb.go index 0502ebbcd8..83f93adf6f 100644 --- a/state/rethinkdb/rethinkdb.go +++ b/state/rethinkdb/rethinkdb.go @@ -15,7 +15,6 @@ package rethinkdb import ( "context" - "crypto/tls" "encoding/json" "errors" "fmt" @@ -33,12 +32,9 @@ import ( ) const ( - stateTableNameDefault = "daprstate" - // TODO: this needs to be exposed as a metadata option? - stateTablePKName = "id" - // TODO: this needs to be exposed as a metadata option - stateArchiveTableName = "daprstate_archive" - // TODO: this needs to be exposed as a metadata option? + stateTableNameDefault = "daprstate" + stateTablePKName = "id" + stateArchiveTableName = "daprstate_archive" stateArchiveTablePKName = "key" ) @@ -51,38 +47,9 @@ type RethinkDB struct { } type stateConfig struct { - ConnectOptsWrapper `mapstructure:",squash"` - Archive bool `json:"archive"` - Table string `json:"table"` -} - -// ConnectOptsWrapper wraps r.ConnectOpts but excludes TLSConfig -// This is needed because the metadata decoder does not support nested structs with tags as inputs in the metadata.yaml file -type ConnectOptsWrapper struct { - Address string `gorethink:"address,omitempty"` - Addresses []string `gorethink:"addresses,omitempty"` - Database string `gorethink:"database,omitempty"` - Username string `gorethink:"username,omitempty"` - Password string `gorethink:"password,omitempty"` - AuthKey string `gorethink:"authkey,omitempty"` - Timeout time.Duration `gorethink:"timeout,omitempty"` - WriteTimeout time.Duration `gorethink:"write_timeout,omitempty"` - ReadTimeout time.Duration `gorethink:"read_timeout,omitempty"` - KeepAlivePeriod time.Duration `gorethink:"keep_alive_timeout,omitempty"` - HandshakeVersion int `gorethink:"handshake_version,omitempty"` - MaxIdle int `gorethink:"max_idle,omitempty"` - InitialCap int `gorethink:"initial_cap,omitempty"` - MaxOpen int `gorethink:"max_open,omitempty"` - DiscoverHosts bool `gorethink:"discover_hosts,omitempty"` - NodeRefreshInterval time.Duration `gorethink:"node_refresh_interval,omitempty"` - UseJSONNumber bool `gorethink:"use_json_number,omitempty"` - NumRetries int `gorethink:"num_retries,omitempty"` - HostDecayDuration time.Duration `gorethink:"host_decay_duration,omitempty"` - UseOpentracing bool `gorethink:"use_opentracing,omitempty"` - // TLS fields must be brought in as separate fields as they will not be processed by the metadata decoder properly without this - EnableTLS bool `gorethink:"enable_tls,omitempty"` - ClientCert string `gorethink:"client_cert,omitempty"` - ClientKey string `gorethink:"client_key,omitempty"` + r.ConnectOpts `mapstructure:",squash"` + Archive bool `json:"archive"` + Table string `json:"table"` } type stateRecord struct { @@ -114,41 +81,7 @@ func (s *RethinkDB) Init(ctx context.Context, metadata state.Metadata) error { if s.session != nil && s.session.IsConnected() { s.session.Close() } - - // Convert wrapper to r.ConnectOpts - connectOpts := r.ConnectOpts{ - Address: cfg.Address, - Addresses: cfg.Addresses, - Database: cfg.Database, - Username: cfg.Username, - Password: cfg.Password, - AuthKey: cfg.AuthKey, - Timeout: cfg.Timeout, - WriteTimeout: cfg.WriteTimeout, - ReadTimeout: cfg.ReadTimeout, - KeepAlivePeriod: cfg.KeepAlivePeriod, - HandshakeVersion: r.HandshakeVersion(cfg.HandshakeVersion), - MaxIdle: cfg.MaxIdle, - InitialCap: cfg.InitialCap, - MaxOpen: cfg.MaxOpen, - DiscoverHosts: cfg.DiscoverHosts, - NodeRefreshInterval: cfg.NodeRefreshInterval, - UseJSONNumber: cfg.UseJSONNumber, - NumRetries: cfg.NumRetries, - HostDecayDuration: cfg.HostDecayDuration, - UseOpentracing: cfg.UseOpentracing, - } - - // Configure TLS if enabled - if cfg.EnableTLS { - tlsConfig, tlsErr := createTLSConfig(cfg.ClientCert, cfg.ClientKey) - if tlsErr != nil { - return fmt.Errorf("error creating TLS config: %w", tlsErr) - } - connectOpts.TLSConfig = tlsConfig - } - - ses, err := r.Connect(connectOpts) + ses, err := r.Connect(cfg.ConnectOpts) if err != nil { return fmt.Errorf("error connecting to the database: %w", err) } @@ -374,23 +307,6 @@ func metadataToConfig(cfg map[string]string, logger logger.Logger) (*stateConfig return &c, nil } -// createTLSConfig creates a tls.Config from client certificate and key -func createTLSConfig(clientCert, clientKey string) (*tls.Config, error) { - if clientCert == "" || clientKey == "" { - return nil, errors.New("both client certificate and key are required for TLS") - } - - cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey)) - if err != nil { - return nil, fmt.Errorf("error parsing client certificate and key: %w", err) - } - - return &tls.Config{ - Certificates: []tls.Certificate{cert}, - MinVersion: tls.VersionTLS12, - }, nil -} - func (s *RethinkDB) GetComponentMetadata() (metadataInfo metadata.MetadataMap) { metadataStruct := stateConfig{} metadata.GetMetadataInfoFromStructType(reflect.TypeOf(metadataStruct), &metadataInfo, metadata.StateStoreType) diff --git a/state/sqlite/metadata.yaml b/state/sqlite/metadata.yaml deleted file mode 100644 index 69289f8ee6..0000000000 --- a/state/sqlite/metadata.yaml +++ /dev/null @@ -1,53 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: state -name: sqlite -version: v1 -status: stable -title: "SQLite" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-sqlite/ -authenticationProfiles: - - title: "Connection String" - description: "Authenticate using a connection string." - metadata: - - name: connectionString - type: string - required: true - description: The SQLite database connection string. -metadata: - - name: timeout - type: string - required: false - description: Timeout for database requests in seconds. - example: "20s" - default: "20s" - - name: busyTimeout - type: string - required: false - description: Busy timeout for database operations in seconds. - example: "2s" - default: "2s" - - name: disableWAL - type: bool - required: false - description: Disable WAL journaling. Should not use WAL if database is stored on a network filesystem. - example: false - default: false - - name: tableName - type: string - required: false - description: The name of the table to store state data. - example: "state" - - name: metadataTableName - type: string - required: false - description: The name of the table to store metadata. - example: "metadata" - - name: cleanupInterval - type: string - required: false - description: Interval for cleanup operations in seconds. Set to 0 to disable. - example: "0s" - default: "0s" \ No newline at end of file diff --git a/state/sqlserver/sqlserver.go b/state/sqlserver/sqlserver.go index f607d47af1..35e2f7ed20 100644 --- a/state/sqlserver/sqlserver.go +++ b/state/sqlserver/sqlserver.go @@ -16,7 +16,6 @@ package sqlserver import ( "context" "database/sql" - "encoding/base64" "encoding/hex" "encoding/json" "errors" @@ -288,15 +287,8 @@ func (s *SQLServer) Get(ctx context.Context, req *state.GetRequest) (*state.GetR } } - bytes, err := base64.StdEncoding.DecodeString(data) - if err != nil { - s.logger. - WithFields(map[string]any{"error": err}). - Debug("error decoding base64 data. Fallback to []byte") - bytes = []byte(data) - } return &state.GetResponse{ - Data: bytes, + Data: []byte(data), ETag: ptr.Of(etag), Metadata: metadata, }, nil @@ -313,23 +305,16 @@ type dbExecutor interface { } func (s *SQLServer) executeSet(ctx context.Context, db dbExecutor, req *state.SetRequest) error { - var reqValue string - - bytes, ok := req.Value.([]byte) - if !ok { - bt, err := json.Marshal(req.Value) - if err != nil { - return err - } - reqValue = string(bt) - } else { - reqValue = base64.StdEncoding.EncodeToString(bytes) + var err error + var bytes []byte + bytes, err = utils.Marshal(req.Value, json.Marshal) + if err != nil { + return err } - etag := sql.Named(rowVersionColumnName, nil) if req.HasETag() { var b []byte - b, err := hex.DecodeString(*req.ETag) + b, err = hex.DecodeString(*req.ETag) if err != nil { return state.NewETagError(state.ETagInvalid, err) } @@ -342,14 +327,13 @@ func (s *SQLServer) executeSet(ctx context.Context, db dbExecutor, req *state.Se } var res sql.Result - var err error if req.Options.Concurrency == state.FirstWrite { res, err = db.ExecContext(ctx, s.upsertCommand, sql.Named(keyColumnName, req.Key), - sql.Named("Data", reqValue), etag, + sql.Named("Data", string(bytes)), etag, sql.Named("FirstWrite", 1), sql.Named("TTL", ttl)) } else { res, err = db.ExecContext(ctx, s.upsertCommand, sql.Named(keyColumnName, req.Key), - sql.Named("Data", reqValue), etag, + sql.Named("Data", string(bytes)), etag, sql.Named("FirstWrite", 0), sql.Named("TTL", ttl)) } diff --git a/state/sqlserver/sqlserver_integration_test.go b/state/sqlserver/sqlserver_integration_test.go index e317128a40..7af6220476 100644 --- a/state/sqlserver/sqlserver_integration_test.go +++ b/state/sqlserver/sqlserver_integration_test.go @@ -30,12 +30,10 @@ import ( "testing" "time" - "github.com/google/uuid" + uuid "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" - "github.com/dapr/components-contrib/common/proto/state/sqlserver" "github.com/dapr/components-contrib/metadata" "github.com/dapr/components-contrib/state" "github.com/dapr/kit/logger" @@ -44,7 +42,7 @@ import ( const ( // connectionStringEnvKey defines the key containing the integration test connection string // To use docker, server=localhost;user id=sa;password=Pass@Word1;port=1433; - // To use Azure SQL, server=.database.windows.net;User id=;port=1433;password=;database=dapr_test;. + // To use Azure SQL, server=.database.windows.net;user id=;port=1433;password=;database=dapr_test;. connectionStringEnvKey = "DAPR_TEST_SQL_CONNSTRING" usersTableName = "Users" beverageTea = "tea" @@ -79,7 +77,6 @@ func TestIntegrationCases(t *testing.T) { t.Run("Multi operations", testMultiOperations) t.Run("Insert and Update Set Record Dates", testInsertAndUpdateSetRecordDates) t.Run("Multiple initializations", testMultipleInitializations) - t.Run("Should preserve byte data when not base64 encoded", testNonBase64ByteData) // Run concurrent set tests 10 times const executions = 10 @@ -115,9 +112,6 @@ func createMetadata(schema string, kt KeyType, indexedProperties string) state.M // Ensure the database is running // For docker, use: docker run --name sqlserver -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=Pass@Word1" -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-GA-ubuntu-16.04. -// For azure-sql-edge use: -// docker volume create sqlvolume -// docker run --name sqlserver -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=Pass@Word1" -e "MSSQL_PID=Developer" -e "MSSQL_AGENT_ENABLED=TRUE" -e "MSSQL_COLLATION=SQL_Latin1_General_CP1_CI_AS" -e "MSSQL_LCID=1033" -p 1433:1433 -v sqlvolume:/var/opt/mssql -d mcr.microsoft.com/azure-sql-edge:latest func getTestStore(t *testing.T, indexedProperties string) *SQLServer { return getTestStoreWithKeyType(t, StringKeyType, indexedProperties) } @@ -603,23 +597,3 @@ func testMultipleInitializations(t *testing.T) { }) } } - -func testNonBase64ByteData(t *testing.T) { - t.Run("Set And Get", func(t *testing.T) { - store := getTestStore(t, "") - request := &sqlserver.TestEvent{ - EventId: -1, - } - requestBytes, err := proto.Marshal(request) - require.NoError(t, err) - require.NoError(t, store.Set(t.Context(), &state.SetRequest{Key: "1", Value: requestBytes})) - resp, err := store.Get(t.Context(), &state.GetRequest{Key: "1"}) - require.NoError(t, err) - - response := &sqlserver.TestEvent{} - err = proto.Unmarshal(resp.Data, response) - require.NoError(t, err) - - assert.EqualValues(t, request.GetEventId(), response.GetEventId()) - }) -} diff --git a/state/zookeeper/metadata.yaml b/state/zookeeper/metadata.yaml deleted file mode 100644 index 10bc49bff7..0000000000 --- a/state/zookeeper/metadata.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: state -name: zookeeper -version: v1 -status: alpha -title: "ZooKeeper" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-zookeeper/ -metadata: - - name: servers - type: string - required: true - description: Comma-separated list of ZooKeeper servers. - example: "localhost:2181" - - name: sessionTimeout - type: string - required: true - description: Session timeout in seconds. - example: "10s" - - name: maxBufferSize - type: number - required: false - description: The maximum buffer size in bytes. - example: 1048576 - default: 1048576 # 1MB - - name: maxConnBufferSize - type: number - required: false - description: The maximum connection buffer size in bytes. - example: 1048576 - default: 1048576 # 1MB - - name: keyPrefixPath - type: string - required: false - description: The key prefix path to use. - example: "my_key_prefix_path" - default: "" \ No newline at end of file diff --git a/state/zookeeper/zk.go b/state/zookeeper/zk.go index 52bd54793a..418a44b8d3 100644 --- a/state/zookeeper/zk.go +++ b/state/zookeeper/zk.go @@ -32,7 +32,6 @@ import ( "github.com/dapr/kit/ptr" ) -// TODO: I think we need more defaults set on these metadata fields. const ( anyVersion = -1 defaultMaxBufferSize = 1024 * 1024 From 7e19011594eca0bb9f9e5872a2abddee2013602e Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Mon, 11 Aug 2025 09:59:04 -0500 Subject: [PATCH 24/32] revert: checkout bindings dir from main since in diff pr now Signed-off-by: Samantha Coyle --- .../alicloud/dingtalk/webhook/metadata.yaml | 31 ---- bindings/alicloud/oss/metadata.yaml | 49 ------ bindings/alicloud/sls/metadata.yaml | 52 ------ bindings/alicloud/sls/sls.go | 1 - bindings/alicloud/tablestore/metadata.yaml | 48 ------ bindings/alicloud/tablestore/tablestore.go | 4 +- bindings/apns/apns.go | 2 - bindings/apns/metadata.yaml | 41 ----- bindings/aws/dynamodb/dynamodb.go | 1 - bindings/aws/dynamodb/metadata.yaml | 48 ------ bindings/aws/kinesis/kinesis.go | 3 +- bindings/aws/kinesis/metadata.yaml | 66 -------- bindings/aws/ses/metadata.yaml | 60 ------- bindings/aws/ses/ses.go | 1 - bindings/aws/sqs/metadata.yaml | 50 ------ bindings/aws/sqs/sqs.go | 1 - .../cosmosdbgremlinapi.go | 0 .../cosmosdbgremlinapi_test.go | 0 .../metadata.yaml | 0 .../servicebusqueues/servicebusqueues.go | 2 +- bindings/cloudflare/queues/metadata.yaml | 68 -------- bindings/commercetools/commercetools.go | 12 +- bindings/commercetools/metadata.yaml | 49 ------ bindings/dubbo/metadata.yaml | 41 ----- bindings/gcp/bucket/bucket.go | 43 +---- bindings/gcp/bucket/bucket_test.go | 25 --- bindings/gcp/pubsub/metadata.yaml | 29 ---- bindings/gcp/pubsub/pubsub.go | 90 ++-------- bindings/gcp/pubsub/pubsub_test.go | 27 ++- bindings/graphql/metadata.yaml | 21 --- bindings/huawei/obs/metadata.yaml | 43 ----- bindings/influx/metadata.yaml | 43 ----- bindings/kitex/metadata.yaml | 33 ---- bindings/kubemq/metadata.yaml | 56 ------- bindings/kubernetes/kubernetes.go | 6 +- bindings/kubernetes/metadata.yaml | 33 ---- bindings/localstorage/metadata.yaml | 25 --- bindings/mqtt3/metadata.go | 18 +- bindings/mqtt3/metadata.yaml | 64 ------- bindings/postmark/metadata.yaml | 45 ----- bindings/redis/metadata.yaml | 26 +-- bindings/rethinkdb/statechange/metadata.yaml | 158 ------------------ bindings/rethinkdb/statechange/statechange.go | 91 +--------- bindings/rocketmq/metadata.yaml | 77 --------- bindings/rocketmq/settings.go | 2 +- bindings/sftp/metadata.yaml | 80 --------- bindings/smtp/metadata.yaml | 70 -------- bindings/twilio/sendgrid/metadata.yaml | 63 ------- bindings/twilio/sms/metadata.yaml | 45 ----- bindings/twilio/sms/sms.go | 4 + bindings/wasm/metadata.yaml | 26 --- 51 files changed, 66 insertions(+), 1807 deletions(-) delete mode 100644 bindings/alicloud/dingtalk/webhook/metadata.yaml delete mode 100644 bindings/alicloud/oss/metadata.yaml delete mode 100644 bindings/alicloud/sls/metadata.yaml delete mode 100644 bindings/alicloud/tablestore/metadata.yaml delete mode 100644 bindings/apns/metadata.yaml delete mode 100644 bindings/aws/dynamodb/metadata.yaml delete mode 100644 bindings/aws/kinesis/metadata.yaml delete mode 100644 bindings/aws/ses/metadata.yaml delete mode 100644 bindings/aws/sqs/metadata.yaml rename bindings/azure/{cosmosdb/gremlinapi => cosmosdbgremlinapi}/cosmosdbgremlinapi.go (100%) rename bindings/azure/{cosmosdb/gremlinapi => cosmosdbgremlinapi}/cosmosdbgremlinapi_test.go (100%) rename bindings/azure/{cosmosdb/gremlinapi => cosmosdbgremlinapi}/metadata.yaml (100%) delete mode 100644 bindings/cloudflare/queues/metadata.yaml delete mode 100644 bindings/commercetools/metadata.yaml delete mode 100644 bindings/dubbo/metadata.yaml delete mode 100644 bindings/gcp/pubsub/metadata.yaml delete mode 100644 bindings/graphql/metadata.yaml delete mode 100644 bindings/huawei/obs/metadata.yaml delete mode 100644 bindings/influx/metadata.yaml delete mode 100644 bindings/kitex/metadata.yaml delete mode 100644 bindings/kubemq/metadata.yaml delete mode 100644 bindings/kubernetes/metadata.yaml delete mode 100644 bindings/localstorage/metadata.yaml delete mode 100644 bindings/mqtt3/metadata.yaml delete mode 100644 bindings/postmark/metadata.yaml delete mode 100644 bindings/rethinkdb/statechange/metadata.yaml delete mode 100644 bindings/rocketmq/metadata.yaml delete mode 100644 bindings/sftp/metadata.yaml delete mode 100644 bindings/smtp/metadata.yaml delete mode 100644 bindings/twilio/sendgrid/metadata.yaml delete mode 100644 bindings/twilio/sms/metadata.yaml delete mode 100644 bindings/wasm/metadata.yaml diff --git a/bindings/alicloud/dingtalk/webhook/metadata.yaml b/bindings/alicloud/dingtalk/webhook/metadata.yaml deleted file mode 100644 index 4d88ff3ef8..0000000000 --- a/bindings/alicloud/dingtalk/webhook/metadata.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: alicloud.dingtalk.webhook -version: v1 -status: alpha -title: "AliCloud DingTalk Webhook" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/alicloud-dingtalk/ -binding: - output: true - input: true - operations: - - name: create - description: "Send a message to DingTalk webhook" - - name: read - description: "Receive messages from DingTalk webhook" -metadata: - - name: id - required: true - description: "The webhook ID" - example: '"your-webhook-id"' - - name: url - required: true - description: "The webhook URL" - example: '"https://oapi.dingtalk.com/robot/send?access_token=your-token"' - - name: secret - required: false - description: "The webhook secret for signature verification" - example: '"your-webhook-secret"' \ No newline at end of file diff --git a/bindings/alicloud/oss/metadata.yaml b/bindings/alicloud/oss/metadata.yaml deleted file mode 100644 index 6746615f68..0000000000 --- a/bindings/alicloud/oss/metadata.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: alicloud.oss -version: v1 -status: alpha -title: "AliCloud Object Storage Service (OSS)" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/oss/ -binding: - output: true - input: false - operations: - - name: create - description: "Upload file to OSS" -authenticationProfiles: - - title: "AliCloud Access Key Authentication" - description: | - Authenticate using AliCloud access key credentials. - metadata: - - name: accessKey - required: false - description: "The AliCloud access key" - example: '"your-access-key"' - sensitive: true - - name: accessKeyID - required: false - description: "The AliCloud access key ID" - example: '"your-access-key-id"' - sensitive: true - - name: accessKeySecret - required: false - sensitive: true - description: "The AliCloud access key secret" - example: '"your-access-key-secret"' -metadata: - - name: endpoint - required: true - description: "The OSS endpoint" - example: '"https://oss-cn-hangzhou.aliyuncs.com"' - - name: bucket - required: true - description: "The OSS bucket name" - example: '"your-bucket-name"' - - name: objectKey - required: true - description: "The object key in the bucket" - example: '"path/to/file.txt"' \ No newline at end of file diff --git a/bindings/alicloud/sls/metadata.yaml b/bindings/alicloud/sls/metadata.yaml deleted file mode 100644 index e7545c84f5..0000000000 --- a/bindings/alicloud/sls/metadata.yaml +++ /dev/null @@ -1,52 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: alicloud.sls -version: v1 -status: alpha -title: "AliCloud Simple Log Storage (SLS)" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/alicloudsls/ -binding: - output: true - input: false - operations: - - name: create - description: "Send logs to SLS" -authenticationProfiles: - - title: "Access Key Authentication" - description: | - Authenticate using AliCloud access key credentials. - metadata: - - name: accessKeyID - required: false - sensitive: true - description: "The AliCloud access key ID" - example: '"your-access-key-id"' - - name: accessKeySecret - required: false - sensitive: true - description: "The AliCloud access key secret" - example: '"your-access-key-secret"' -metadata: - - name: endpoint - required: true - description: "The SLS endpoint" - example: '"https://your-project.cn-hangzhou.log.aliyuncs.com"' - - name: project - required: true - description: "The SLS project name" - example: '"your-project-name"' - - name: logstore - required: true - description: "The SLS logstore name" - example: '"your-logstore-name"' - - name: topic - required: true - description: "The SLS topic name" - example: '"your-topic-name"' - - name: source - required: true - description: "The SLS source name" - example: '"your-source-name"' \ No newline at end of file diff --git a/bindings/alicloud/sls/sls.go b/bindings/alicloud/sls/sls.go index 9cc983736f..42d7b4a55c 100644 --- a/bindings/alicloud/sls/sls.go +++ b/bindings/alicloud/sls/sls.go @@ -60,7 +60,6 @@ func NewAliCloudSlsLogstorage(logger logger.Logger) bindings.OutputBinding { func (s *AliCloudSlsLogstorage) Invoke(ctx context.Context, req *bindings.InvokeRequest) (*bindings.InvokeResponse, error) { // verify the metadata property - // TODO: move these to the struct with proper tags if logProject := req.Metadata["project"]; logProject == "" { return nil, errors.New("SLS binding error: project property not supplied") } diff --git a/bindings/alicloud/tablestore/metadata.yaml b/bindings/alicloud/tablestore/metadata.yaml deleted file mode 100644 index 80ab4bc051..0000000000 --- a/bindings/alicloud/tablestore/metadata.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: alicloud.tablestore -version: v1 -status: stable -title: "AliCloud Table Store" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/alicloudtablestore/ -binding: - output: true - input: false - operations: - - name: create - description: "Write data to Table Store" -authenticationProfiles: - - title: "Access Key Authentication" - description: | - Authenticate using AliCloud access key credentials. - metadata: - - name: accessKey - required: false - description: "The AliCloud access key" - example: '"your-access-key"' - sensitive: true - - name: accessKeyID - required: true - description: "The AliCloud access key ID" - example: '"your-access-key-id"' - - name: accessKeySecret - required: true - sensitive: true - description: "The AliCloud access key secret" - example: '"your-access-key-secret"' -metadata: - - name: endpoint - required: true - description: "The Table Store endpoint" - example: '"https://your-instance.cn-hangzhou.ots.aliyuncs.com"' - - name: instanceName - required: true - description: "The Table Store instance name" - example: '"your-instance-name"' - - name: tableName - required: true - description: "The table name to write to" - example: '"your-table-name"' \ No newline at end of file diff --git a/bindings/alicloud/tablestore/tablestore.go b/bindings/alicloud/tablestore/tablestore.go index e8762e8487..c153f2c1e0 100644 --- a/bindings/alicloud/tablestore/tablestore.go +++ b/bindings/alicloud/tablestore/tablestore.go @@ -137,7 +137,7 @@ func (s *AliCloudTableStore) get(req *bindings.InvokeRequest, resp *bindings.Inv pkNames := strings.Split(req.Metadata[primaryKeys], ",") pks := make([]*tablestore.PrimaryKeyColumn, len(pkNames)) - data := make(map[string]any) + data := make(map[string]interface{}) err := json.Unmarshal(req.Data, &data) if err != nil { return err @@ -313,7 +313,7 @@ func (s *AliCloudTableStore) unmarshal(pks []*tablestore.PrimaryKeyColumn, colum return nil, nil } - data := make(map[string]any) + data := make(map[string]interface{}) for _, pk := range pks { data[pk.ColumnName] = pk.Value diff --git a/bindings/apns/apns.go b/bindings/apns/apns.go index abb8a99b68..b0a08d4532 100644 --- a/bindings/apns/apns.go +++ b/bindings/apns/apns.go @@ -33,7 +33,6 @@ import ( kitmd "github.com/dapr/kit/metadata" ) -// TODO: these should be configured in the metadata.yaml file and be part of the metadata struct with proper json tags. const ( collapseIDKey = "apns-collapse-id" developmentKey = "development" @@ -68,7 +67,6 @@ type APNS struct { authorizationBuilder *authorizationBuilder } -// TODO: use proper tags type APNSmetadata struct { Development bool `mapstructure:"development"` KeyID string `mapstructure:"key-id"` diff --git a/bindings/apns/metadata.yaml b/bindings/apns/metadata.yaml deleted file mode 100644 index 08b4b8a7f9..0000000000 --- a/bindings/apns/metadata.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: apns -version: v1 -status: alpha -title: "Apple Push Notification Service (APNS)" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/apns/ -binding: - output: true - input: false - operations: - - name: create - description: "Send push notification via APNS" -authenticationProfiles: - - title: "APNS Key Authentication" - description: | - Authenticate using APNS key credentials. - metadata: - - name: key-id - required: true - description: "The APNS key ID" - example: '"ABC123DEF4"' - - name: team-id - required: true - description: "The APNS team ID" - example: '"DEF123GHI4"' - - name: private-key - required: true - sensitive: true - description: "The APNS private key (P8 file content)" - example: '"-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg..."' -metadata: - - name: development - type: bool - required: false - description: "The APNS environment is development or not" - example: true - default: false \ No newline at end of file diff --git a/bindings/aws/dynamodb/dynamodb.go b/bindings/aws/dynamodb/dynamodb.go index 3f20abdcdd..2096f22433 100644 --- a/bindings/aws/dynamodb/dynamodb.go +++ b/bindings/aws/dynamodb/dynamodb.go @@ -36,7 +36,6 @@ type DynamoDB struct { logger logger.Logger } -// TODO: the metadata fields need updating to use the builtin aws auth provider fully and reflect in metadata.yaml type dynamoDBMetadata struct { Region string `json:"region" mapstructure:"region"` Endpoint string `json:"endpoint" mapstructure:"endpoint"` diff --git a/bindings/aws/dynamodb/metadata.yaml b/bindings/aws/dynamodb/metadata.yaml deleted file mode 100644 index d2da104054..0000000000 --- a/bindings/aws/dynamodb/metadata.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: aws.dynamodb -version: v1 -status: stable -title: "AWS DynamoDB" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/dynamodb/ -binding: - output: true - input: false - operations: - - name: create - description: "Write item to DynamoDB table" -authenticationProfiles: - - title: "AWS Access Key Authentication" - description: | - Authenticate using AWS access key credentials. - metadata: - - name: accessKey - required: true - description: "The AWS access key" - example: '"AKIAIOSFODNN7EXAMPLE"' - - name: secretKey - required: true - sensitive: true - description: "The AWS secret key" - example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"' - - name: sessionToken - required: false - sensitive: true - description: "The AWS session token" - example: '"TOKEN"' - - name: region - required: true - description: "The AWS region" - example: '"us-east-1"' -metadata: - - name: table - required: true - description: "The DynamoDB table name" - example: '"my-table"' - - name: endpoint - required: false - description: "The AWS DynamoDB endpoint" - example: '"https://dynamodb.us-east-1.amazonaws.com"' \ No newline at end of file diff --git a/bindings/aws/kinesis/kinesis.go b/bindings/aws/kinesis/kinesis.go index 47ae3ee4ec..bf684f8bbb 100644 --- a/bindings/aws/kinesis/kinesis.go +++ b/bindings/aws/kinesis/kinesis.go @@ -55,7 +55,6 @@ type AWSKinesis struct { wg sync.WaitGroup } -// TODO: we need to clean up the metadata fields here and update this binding to use the builtin aws auth provider and reflect in metadata.yaml type kinesisMetadata struct { StreamName string `json:"streamName" mapstructure:"streamName"` ConsumerName string `json:"consumerName" mapstructure:"consumerName"` @@ -74,7 +73,7 @@ const ( // SharedThroughput - shared throughput using checkpoint and monitoring. SharedThroughput = "shared" - partitionKeyName = "partitionKey" // TODO: mv to metadata field instead + partitionKeyName = "partitionKey" ) // recordProcessorFactory. diff --git a/bindings/aws/kinesis/metadata.yaml b/bindings/aws/kinesis/metadata.yaml deleted file mode 100644 index 719d07a7a5..0000000000 --- a/bindings/aws/kinesis/metadata.yaml +++ /dev/null @@ -1,66 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: aws.kinesis -version: v1 -status: alpha -title: "AWS Kinesis" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/kinesis/ -binding: - output: true - input: true - operations: - - name: create - description: "Send record to Kinesis stream" - - name: read - description: "Receive records from Kinesis stream" -authenticationProfiles: - - title: "AWS Access Key Authentication" - description: | - Authenticate using AWS access key credentials. - metadata: - - name: accessKey - required: true - description: "The AWS access key" - example: '"AKIAIOSFODNN7EXAMPLE"' - - name: secretKey - required: true - sensitive: true - description: "The AWS secret key" - example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"' - - name: sessionToken - required: false - sensitive: true - description: "The AWS session token" - example: '"TOKEN"' - - name: region - required: true - description: "The AWS region" - example: '"us-east-1"' -metadata: - - name: streamName - required: true - description: "The Kinesis stream name" - example: '"my-stream"' - - name: consumerName - required: false - description: "The consumer name for input binding" - example: '"my-consumer"' - - name: mode - required: false - description: "The consumer mode" - example: '"shared"' - default: '"shared"' - allowedValues: - - "shared" - - "extended" - - name: partitionKey - required: false - description: "The partition key for the Kinesis stream" - example: '"my-partition-key"' - - name: endpoint - required: false - description: "The AWS Kinesis endpoint" - example: '"https://kinesis.us-east-1.amazonaws.com"' \ No newline at end of file diff --git a/bindings/aws/ses/metadata.yaml b/bindings/aws/ses/metadata.yaml deleted file mode 100644 index d995f112b1..0000000000 --- a/bindings/aws/ses/metadata.yaml +++ /dev/null @@ -1,60 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: aws.ses -version: v1 -status: stable -title: "AWS Simple Email Service (SES)" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/ses/ -binding: - output: true - input: false - operations: - - name: create - description: "Send email via AWS SES" -authenticationProfiles: - - title: "AWS Access Key Authentication" - description: | - Authenticate using AWS access key credentials. - metadata: - - name: accessKey - required: true - description: "The AWS access key" - example: '"AKIAIOSFODNN7EXAMPLE"' - - name: secretKey - required: true - sensitive: true - description: "The AWS secret key" - example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"' - - name: sessionToken - required: false - sensitive: true - description: "The AWS session token" - example: '"TOKEN"' - - name: region - required: true - description: "The AWS region" - example: '"us-east-1"' -metadata: - - name: emailFrom - required: true - description: "The sender email address" - example: '"sender@example.com"' - - name: emailTo - required: true - description: "The recipient email address" - example: '"recipient@example.com"' - - name: subject - required: true - description: "The email subject" - example: '"Hello from Dapr"' - - name: emailCc - required: false - description: "The email CC address" - example: '"cc@example.com"' - - name: emailBcc - required: false - description: "The email BCC address" - example: '"bcc@example.com"' \ No newline at end of file diff --git a/bindings/aws/ses/ses.go b/bindings/aws/ses/ses.go index 2fb9300f6e..b8d2ff3faa 100644 --- a/bindings/aws/ses/ses.go +++ b/bindings/aws/ses/ses.go @@ -43,7 +43,6 @@ type AWSSES struct { logger logger.Logger } -// TODO: the metadata fields need updating to use the builtin aws auth provider fully and reflect in metadata.yaml type sesMetadata struct { Region string `json:"region"` AccessKey string `json:"accessKey"` diff --git a/bindings/aws/sqs/metadata.yaml b/bindings/aws/sqs/metadata.yaml deleted file mode 100644 index 6a05b4a214..0000000000 --- a/bindings/aws/sqs/metadata.yaml +++ /dev/null @@ -1,50 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: aws.sqs -version: v1 -status: alpha -title: "AWS SQS" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/sqs/ -binding: - output: true - input: true - operations: - - name: create - description: "Send message to SQS queue" - - name: read - description: "Receive messages from SQS queue" -authenticationProfiles: - - title: "AWS Access Key Authentication" - description: | - Authenticate using AWS access key credentials. - metadata: - - name: accessKey - required: true - description: "The AWS access key" - example: '"AKIAIOSFODNN7EXAMPLE"' - - name: secretKey - required: true - sensitive: true - description: "The AWS secret key" - example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"' - - name: sessionToken - required: false - sensitive: true - description: "The AWS session token" - example: '"TOKEN"' - - name: region - required: true - description: "The AWS region" - example: '"us-east-1"' -metadata: - - name: queueName - required: true - description: "The SQS queue name" - example: '"my-queue"' - - name: endpoint - required: false - description: "The AWS SQS endpoint" - example: '"https://sqs.us-east-1.amazonaws.com"' \ No newline at end of file diff --git a/bindings/aws/sqs/sqs.go b/bindings/aws/sqs/sqs.go index 7d979f009e..b09fde61f6 100644 --- a/bindings/aws/sqs/sqs.go +++ b/bindings/aws/sqs/sqs.go @@ -41,7 +41,6 @@ type AWSSQS struct { closed atomic.Bool } -// TODO: the metadata fields need updating to use the builtin aws auth provider fully and reflect in metadata.yaml type sqsMetadata struct { QueueName string `json:"queueName"` Region string `json:"region"` diff --git a/bindings/azure/cosmosdb/gremlinapi/cosmosdbgremlinapi.go b/bindings/azure/cosmosdbgremlinapi/cosmosdbgremlinapi.go similarity index 100% rename from bindings/azure/cosmosdb/gremlinapi/cosmosdbgremlinapi.go rename to bindings/azure/cosmosdbgremlinapi/cosmosdbgremlinapi.go diff --git a/bindings/azure/cosmosdb/gremlinapi/cosmosdbgremlinapi_test.go b/bindings/azure/cosmosdbgremlinapi/cosmosdbgremlinapi_test.go similarity index 100% rename from bindings/azure/cosmosdb/gremlinapi/cosmosdbgremlinapi_test.go rename to bindings/azure/cosmosdbgremlinapi/cosmosdbgremlinapi_test.go diff --git a/bindings/azure/cosmosdb/gremlinapi/metadata.yaml b/bindings/azure/cosmosdbgremlinapi/metadata.yaml similarity index 100% rename from bindings/azure/cosmosdb/gremlinapi/metadata.yaml rename to bindings/azure/cosmosdbgremlinapi/metadata.yaml diff --git a/bindings/azure/servicebusqueues/servicebusqueues.go b/bindings/azure/servicebusqueues/servicebusqueues.go index bc1291ac32..af2bc3b534 100644 --- a/bindings/azure/servicebusqueues/servicebusqueues.go +++ b/bindings/azure/servicebusqueues/servicebusqueues.go @@ -62,7 +62,7 @@ func (a *AzureServiceBusQueues) Init(ctx context.Context, metadata bindings.Meta return err } - a.client, err = impl.NewClient(a.metadata, metadata.Properties, a.logger) + a.client, err = impl.NewClient(a.metadata, metadata.Properties) if err != nil { return err } diff --git a/bindings/cloudflare/queues/metadata.yaml b/bindings/cloudflare/queues/metadata.yaml deleted file mode 100644 index 4c6c5310d6..0000000000 --- a/bindings/cloudflare/queues/metadata.yaml +++ /dev/null @@ -1,68 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: cloudflare.queues -version: v1 -status: alpha -title: "Cloudflare Queues" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/cloudflare-queues/ -binding: - output: true - input: false - operations: - - name: create - description: "Send message to Cloudflare Queue" - - name: read - description: "Receive messages from Cloudflare Queue" -authenticationProfiles: - - title: "API Token Authentication" - description: | - Authenticate using Cloudflare API token and account ID. Dapr will create/manage the worker. - metadata: - - name: cfAPIToken - required: true - sensitive: true - description: "The Cloudflare API token" - example: '"your-api-token"' - - name: cfAccountID - required: true - description: "The Cloudflare account ID" - example: '"your-account-id"' - - name: key - required: true - sensitive: true - description: "The Ed25519 private key in PKCS#8 PEM format for JWT signing" - example: '"-----BEGIN PRIVATE KEY-----\nXXX..."' - - name: workerName - required: true - description: "The worker name for JWT token audience" - example: '"my-worker"' - - title: "Connect to Pre-deployed Worker" - description: | - Connect to a worker that has been pre-deployed and is ready to use. No API tokens needed. - metadata: - - name: workerUrl - required: true - description: "The Cloudflare worker URL" - example: '"https://your-worker.your-subdomain.workers.dev"' - - name: key - required: true - sensitive: true - description: "The Ed25519 private key in PKCS#8 PEM format for JWT signing" - example: '"-----BEGIN PRIVATE KEY-----\nXXX..."' - - name: workerName - required: true - description: "The worker name for JWT token audience" - example: '"my-worker"' -metadata: - - name: timeoutInSeconds - required: false - description: "Timeout for network requests in seconds" - example: '20' - default: '20' - - name: queueName - required: true - description: "The Cloudflare queue name" - example: '"my-queue"' \ No newline at end of file diff --git a/bindings/commercetools/commercetools.go b/bindings/commercetools/commercetools.go index fe36540f5a..d48acba52b 100644 --- a/bindings/commercetools/commercetools.go +++ b/bindings/commercetools/commercetools.go @@ -40,12 +40,12 @@ type Data struct { } type commercetoolsMetadata struct { - Region string `json:"region"` - Provider string `json:"provider"` - ProjectKey string `json:"projectKey"` - ClientID string `json:"clientID"` - ClientSecret string `json:"clientSecret"` - Scopes string `json:"scopes"` + Region string + Provider string + ProjectKey string + ClientID string + ClientSecret string + Scopes string } func NewCommercetools(logger logger.Logger) bindings.OutputBinding { diff --git a/bindings/commercetools/metadata.yaml b/bindings/commercetools/metadata.yaml deleted file mode 100644 index c664efcd1c..0000000000 --- a/bindings/commercetools/metadata.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: commercetools -version: v1 -status: alpha -title: "Commercetools" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/commercetools/ -binding: - output: true - input: false - operations: - - name: create - description: "Create resource in Commercetools" -authenticationProfiles: - - title: "OAuth Client Authentication" - description: | - Authenticate using OAuth client credentials. - metadata: - - name: clientID - required: true - description: "The Commercetools client ID" - example: '"your-client-id"' - - name: clientSecret - required: true - sensitive: true - description: "The Commercetools client secret" - example: '"your-client-secret"' -metadata: - - name: projectKey - required: true - description: "The Commercetools project key" - example: '"my-project"' - - name: region - required: true - description: "The Commercetools region" - example: '"gcp-europe-west1"' - default: '"gcp-europe-west1"' - - name: provider - required: true - description: "The Commercetools provider" - example: '"gcp"' - default: '"gcp"' - - name: scopes - required: true - description: "The OAuth scopes" - example: '"manage_project:my-project"' diff --git a/bindings/dubbo/metadata.yaml b/bindings/dubbo/metadata.yaml deleted file mode 100644 index 903b7b3446..0000000000 --- a/bindings/dubbo/metadata.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: dubbo -version: v1 -status: alpha -title: "Apache Dubbo" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/ -binding: - output: true - input: false - operations: - - name: create - description: "Invoke Dubbo service" -metadata: - - name: interfaceName - required: true - description: "The Dubbo interface name" - example: '"com.example.UserService"' - - name: methodName - required: true - description: "The method name to invoke" - example: '"getUser"' - - name: version - required: false - description: "The service version" - example: '"1.0.0"' - - name: group - required: false - description: "The service group" - example: '"mygroup"' - - name: providerHostname - required: false - description: "The provider hostname" - example: '"localhost"' - - name: providerPort - required: false - description: "The provider port" - example: '8080' \ No newline at end of file diff --git a/bindings/gcp/bucket/bucket.go b/bindings/gcp/bucket/bucket.go index 027702f572..155d96abea 100644 --- a/bindings/gcp/bucket/bucket.go +++ b/bindings/gcp/bucket/bucket.go @@ -110,7 +110,13 @@ func (g *GCPStorage) Init(ctx context.Context, metadata bindings.Metadata) error return err } - client, err := g.getClient(ctx, m) + b, err := json.Marshal(m) + if err != nil { + return err + } + + clientOptions := option.WithCredentialsJSON(b) + client, err := storage.NewClient(ctx, clientOptions) if err != nil { return err } @@ -121,41 +127,6 @@ func (g *GCPStorage) Init(ctx context.Context, metadata bindings.Metadata) error return nil } -func (g *GCPStorage) getClient(ctx context.Context, m *gcpMetadata) (*storage.Client, error) { - var client *storage.Client - var err error - - if m.Bucket == "" { - return nil, errors.New("missing property `bucket` in metadata") - } - if m.ProjectID == "" { - return nil, errors.New("missing property `project_id` in metadata") - } - - // Explicit authentication - if m.PrivateKeyID != "" { - var b []byte - b, err = json.Marshal(m) - if err != nil { - return nil, err - } - - clientOptions := option.WithCredentialsJSON(b) - client, err = storage.NewClient(ctx, clientOptions) - if err != nil { - return nil, err - } - } else { - // Implicit authentication, using GCP Application Default Credentials (ADC) - // Credentials search order: https://cloud.google.com/docs/authentication/application-default-credentials#order - client, err = storage.NewClient(ctx) - if err != nil { - return nil, err - } - } - return client, nil -} - func (g *GCPStorage) parseMetadata(meta bindings.Metadata) (*gcpMetadata, error) { m := gcpMetadata{} err := kitmd.DecodeMetadata(meta.Properties, &m) diff --git a/bindings/gcp/bucket/bucket_test.go b/bindings/gcp/bucket/bucket_test.go index 09392f044e..e5200c00c6 100644 --- a/bindings/gcp/bucket/bucket_test.go +++ b/bindings/gcp/bucket/bucket_test.go @@ -15,7 +15,6 @@ package bucket import ( "encoding/json" - "errors" "testing" "github.com/stretchr/testify/assert" @@ -235,30 +234,6 @@ func TestMergeWithRequestMetadata(t *testing.T) { }) } -func TestInit(t *testing.T) { - t.Run("Init missing bucket from metadata", func(t *testing.T) { - m := bindings.Metadata{} - m.Properties = map[string]string{ - "projectID": "my_project_id", - } - gs := GCPStorage{logger: logger.NewLogger("test")} - err := gs.Init(t.Context(), m) - require.Error(t, err) - assert.Equal(t, err, errors.New("missing property `bucket` in metadata")) - }) - - t.Run("Init missing projectID from metadata", func(t *testing.T) { - m := bindings.Metadata{} - m.Properties = map[string]string{ - "bucket": "my_bucket", - } - gs := GCPStorage{logger: logger.NewLogger("test")} - err := gs.Init(t.Context(), m) - require.Error(t, err) - assert.Equal(t, err, errors.New("missing property `project_id` in metadata")) - }) -} - func TestGetOption(t *testing.T) { gs := GCPStorage{logger: logger.NewLogger("test")} gs.metadata = &gcpMetadata{} diff --git a/bindings/gcp/pubsub/metadata.yaml b/bindings/gcp/pubsub/metadata.yaml deleted file mode 100644 index 9927ebe2a1..0000000000 --- a/bindings/gcp/pubsub/metadata.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: gcp.pubsub -version: v1 -status: alpha -title: "Google Cloud Pub/Sub" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/pubsub/ -binding: - output: true - input: true - operations: - - name: create - description: "Publish message to Pub/Sub topic" - - name: read - description: "Receive messages from Pub/Sub subscription" -builtinAuthenticationProfiles: - - name: "gcp" -metadata: - - name: topic - required: true - description: "The Pub/Sub topic name" - example: '"my-topic"' - - name: subscription - required: false - description: "The Pub/Sub subscription name" - example: '"my-subscription"' \ No newline at end of file diff --git a/bindings/gcp/pubsub/pubsub.go b/bindings/gcp/pubsub/pubsub.go index 596cf8f03e..dfc8a69787 100644 --- a/bindings/gcp/pubsub/pubsub.go +++ b/bindings/gcp/pubsub/pubsub.go @@ -28,7 +28,6 @@ import ( "github.com/dapr/components-contrib/bindings" contribMetadata "github.com/dapr/components-contrib/metadata" "github.com/dapr/kit/logger" - kitmd "github.com/dapr/kit/metadata" ) const ( @@ -47,28 +46,10 @@ type GCPPubSub struct { wg sync.WaitGroup } -// These JSON tags directly match the builtin auth provider metadata fields for GCP. -// TODO: in future, this needs to use the same setup that pubsub gcp pubsub component uses, -// so we can embed the builtin auth profile instead... type pubSubMetadata struct { - Topic string `json:"topic"` - Subscription string `json:"subscription"` - - // metadata analyzer needs to ignore these fields as they are part of the builtin auth profile - Type string `json:"type" mdignore:"true"` - ProjectID string `json:"projectID" mdignore:"true"` - PrivateKeyID string `json:"privateKeyID" mdignore:"true"` - PrivateKey string `json:"privateKey" mdignore:"true"` - ClientEmail string `json:"clientEmail" mdignore:"true"` - ClientID string `json:"clientID" mdignore:"true"` - AuthURI string `json:"authURI" mdignore:"true"` - TokenURI string `json:"tokenURI" mdignore:"true"` - AuthProviderX509CertURL string `json:"authProviderX509CertURL" mdignore:"true"` - ClientX509CertURL string `json:"clientX509CertURL" mdignore:"true"` -} - -// TODO: in future, we need to clean this up to rm duplication between this and the pubsub gcp pubsub component -type GCPAuthJSON struct { + Topic string `json:"topic"` + Subscription string `json:"subscription"` + Type string `json:"type"` ProjectID string `json:"project_id"` PrivateKeyID string `json:"private_key_id"` PrivateKey string `json:"private_key"` @@ -78,7 +59,6 @@ type GCPAuthJSON struct { TokenURI string `json:"token_uri"` AuthProviderCertURL string `json:"auth_provider_x509_cert_url"` ClientCertURL string `json:"client_x509_cert_url"` - Type string `json:"type"` } // NewGCPPubSub returns a new GCPPubSub instance. @@ -89,74 +69,32 @@ func NewGCPPubSub(logger logger.Logger) bindings.InputOutputBinding { } } -func parseMetadata(metadata bindings.Metadata) (*pubSubMetadata, error) { - result := pubSubMetadata{ - Type: "service_account", - } - - err := kitmd.DecodeMetadata(metadata.Properties, &result) - if err != nil { - return nil, err - } - - if result.ProjectID == "" { - return nil, errors.New("missing attribute projectID") - } - - return &result, nil -} - // Init parses metadata and creates a new Pub Sub client. func (g *GCPPubSub) Init(ctx context.Context, metadata bindings.Metadata) error { - pubsubMeta, err := parseMetadata(metadata) + b, err := g.parseMetadata(metadata) if err != nil { return err } - pubsubClient, err := g.getPubSubClient(ctx, pubsubMeta) + var pubsubMeta pubSubMetadata + err = json.Unmarshal(b, &pubsubMeta) + if err != nil { + return err + } + clientOptions := option.WithCredentialsJSON(b) + pubsubClient, err := pubsub.NewClient(ctx, pubsubMeta.ProjectID, clientOptions) if err != nil { return fmt.Errorf("error creating pubsub client: %s", err) } g.client = pubsubClient - g.metadata = pubsubMeta + g.metadata = &pubsubMeta return nil } -func (g *GCPPubSub) getPubSubClient(_ context.Context, metadata *pubSubMetadata) (*pubsub.Client, error) { - var pubsubClient *pubsub.Client - var err error - - if metadata.PrivateKeyID != "" { - // TODO: validate that all auth json fields are filled - authJSON := &GCPAuthJSON{ - ProjectID: metadata.ProjectID, - PrivateKeyID: metadata.PrivateKeyID, - PrivateKey: metadata.PrivateKey, - ClientEmail: metadata.ClientEmail, - ClientID: metadata.ClientID, - AuthURI: metadata.AuthURI, - TokenURI: metadata.TokenURI, - AuthProviderCertURL: metadata.AuthProviderX509CertURL, - ClientCertURL: metadata.ClientX509CertURL, - Type: metadata.Type, - } - gcpCompatibleJSON, _ := json.Marshal(authJSON) - clientOptions := option.WithCredentialsJSON(gcpCompatibleJSON) - pubsubClient, err = pubsub.NewClient(context.Background(), metadata.ProjectID, clientOptions) - if err != nil { - return pubsubClient, err - } - } else { - // Use implicit credentials - pubsubClient, err = pubsub.NewClient(context.Background(), metadata.ProjectID) - if err != nil { - return pubsubClient, err - } - } - - return pubsubClient, nil +func (g *GCPPubSub) parseMetadata(metadata bindings.Metadata) ([]byte, error) { + return json.Marshal(metadata.Properties) } func (g *GCPPubSub) Read(ctx context.Context, handler bindings.Handler) error { diff --git a/bindings/gcp/pubsub/pubsub_test.go b/bindings/gcp/pubsub/pubsub_test.go index 592f8dedc9..cd4d3f8171 100644 --- a/bindings/gcp/pubsub/pubsub_test.go +++ b/bindings/gcp/pubsub/pubsub_test.go @@ -14,40 +14,35 @@ limitations under the License. package pubsub import ( + "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/dapr/components-contrib/bindings" + "github.com/dapr/kit/logger" ) func TestInit(t *testing.T) { m := bindings.Metadata{} m.Properties = map[string]string{ - "authProviderX509CertURL": "https://auth", - "authURI": "https://auth", - "clientX509CertURL": "https://cert", - "clientEmail": "test@test.com", - "clientID": "id", - "privateKey": "****", - "privateKeyID": "key_id", - "projectID": "project1", - "tokenURI": "https://token", - "type": "serviceaccount", - "topic": "t1", - "subscription": "s1", + "auth_provider_x509_cert_url": "https://auth", "auth_uri": "https://auth", "client_x509_cert_url": "https://cert", "client_email": "test@test.com", "client_id": "id", "private_key": "****", + "private_key_id": "key_id", "project_id": "project1", "token_uri": "https://token", "type": "serviceaccount", "topic": "t1", "subscription": "s1", } + ps := GCPPubSub{logger: logger.NewLogger("test")} + b, err := ps.parseMetadata(m) + require.NoError(t, err) - // Test metadata parsing only - pubsubMeta, err := parseMetadata(m) + var pubsubMeta pubSubMetadata + err = json.Unmarshal(b, &pubsubMeta) require.NoError(t, err) assert.Equal(t, "s1", pubsubMeta.Subscription) assert.Equal(t, "t1", pubsubMeta.Topic) - assert.Equal(t, "https://auth", pubsubMeta.AuthProviderX509CertURL) + assert.Equal(t, "https://auth", pubsubMeta.AuthProviderCertURL) assert.Equal(t, "https://auth", pubsubMeta.AuthURI) - assert.Equal(t, "https://cert", pubsubMeta.ClientX509CertURL) + assert.Equal(t, "https://cert", pubsubMeta.ClientCertURL) assert.Equal(t, "test@test.com", pubsubMeta.ClientEmail) assert.Equal(t, "id", pubsubMeta.ClientID) assert.Equal(t, "****", pubsubMeta.PrivateKey) diff --git a/bindings/graphql/metadata.yaml b/bindings/graphql/metadata.yaml deleted file mode 100644 index 1346675225..0000000000 --- a/bindings/graphql/metadata.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: graphql -version: v1 -status: alpha -title: "GraphQL" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/graphql/ -binding: - output: true - input: false - operations: - - name: create - description: "Execute GraphQL query or mutation" -metadata: - - name: endpoint - required: true - description: "The GraphQL endpoint URL" - example: '"https://api.example.com/graphql"' \ No newline at end of file diff --git a/bindings/huawei/obs/metadata.yaml b/bindings/huawei/obs/metadata.yaml deleted file mode 100644 index 61b980eb89..0000000000 --- a/bindings/huawei/obs/metadata.yaml +++ /dev/null @@ -1,43 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: huawei.obs -version: v1 -status: alpha -title: "Huawei Object Storage Service (OBS)" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/huawei-obs -binding: - output: true - input: false - operations: - - name: create - description: "Upload file to OBS" -authenticationProfiles: - - title: "Access Key Authentication" - description: | - Authenticate using Huawei Cloud access key credentials. - metadata: - - name: accessKey - required: true - description: "The Huawei Cloud access key ID" - example: '"your-access-key-id"' - - name: secretKey - required: true - sensitive: true - description: "The Huawei Cloud secret access key" - example: '"your-secret-access-key"' - - name: region - required: true - description: "The Huawei Cloud region" - example: '"cn-north-4"' -metadata: - - name: endpoint - required: true - description: "The OBS endpoint" - example: '"https://obs.cn-north-4.myhuaweicloud.com"' - - name: bucket - required: true - description: "The OBS bucket name" - example: '"your-bucket-name"' \ No newline at end of file diff --git a/bindings/influx/metadata.yaml b/bindings/influx/metadata.yaml deleted file mode 100644 index ac78147cc7..0000000000 --- a/bindings/influx/metadata.yaml +++ /dev/null @@ -1,43 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: influx -version: v1 -status: alpha -title: "InfluxDB" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/influxdb/ -binding: - output: true - input: false - operations: - - name: create - description: "Write data points to InfluxDB" -authenticationProfiles: - - title: "Token Authentication" - description: | - Authenticate using InfluxDB token. - metadata: - - name: token - required: true - sensitive: true - description: "The InfluxDB authentication token" - example: '"your-influxdb-token"' -metadata: - - name: url - required: true - description: "The InfluxDB server URL" - example: '"http://localhost:8086"' - - name: org - required: true - description: "The InfluxDB organization name" - example: '"your-org"' - - name: bucket - required: true - description: "The InfluxDB bucket name" - example: '"your-bucket"' - - name: measurement - required: true - description: "The measurement name" - example: '"cpu_usage"' \ No newline at end of file diff --git a/bindings/kitex/metadata.yaml b/bindings/kitex/metadata.yaml deleted file mode 100644 index 6585165e15..0000000000 --- a/bindings/kitex/metadata.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: kitex -version: v1 -status: alpha -title: "Kitex" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/kitex/ -binding: - output: true - input: false - operations: - - name: create - description: "Invoke Kitex service" -metadata: - - name: serviceName - required: true - description: "The Kitex service name" - example: '"my-service"' - - name: methodName - required: true - description: "The method name to invoke" - example: '"getUser"' - - name: destService - required: true - description: "The destination service name" - example: '"my-service"' - - name: hostPorts - required: true - description: "The service address" - example: '"localhost:8080"' \ No newline at end of file diff --git a/bindings/kubemq/metadata.yaml b/bindings/kubemq/metadata.yaml deleted file mode 100644 index b5b70d7605..0000000000 --- a/bindings/kubemq/metadata.yaml +++ /dev/null @@ -1,56 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: kubemq -version: v1 -status: beta -title: "KubeMQ" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/kubemq/ -binding: - output: true - input: true - operations: - - name: create - description: "Send message to KubeMQ" - - name: read - description: "Receive messages from KubeMQ" -authenticationProfiles: - - title: "Token Authentication" - description: "Connect to KubeMQ using an authentication token." - metadata: - - name: authToken - type: string - required: true - description: The authentication token for KubeMQ. - example: "your-auth-token" -metadata: - - name: address - type: string - required: true - description: The KubeMQ server address in format host:port. - example: "localhost:50000" - - name: channel - type: string - required: true - description: The KubeMQ channel name. - example: "my-channel" - - name: pollMaxItems - type: number - required: false - description: The maximum number of items to poll. - example: 10 - default: 1 - - name: pollTimeoutSeconds - type: number - required: false - description: The timeout in seconds for polling. - example: 3600 - default: 3600 - - name: autoAcknowledged - type: bool - required: false - description: Whether to automatically acknowledge messages. - example: true - default: false \ No newline at end of file diff --git a/bindings/kubernetes/kubernetes.go b/bindings/kubernetes/kubernetes.go index 0d3bd8a208..f41bc88cba 100644 --- a/bindings/kubernetes/kubernetes.go +++ b/bindings/kubernetes/kubernetes.go @@ -121,7 +121,7 @@ func (k *kubernetesInput) Read(ctx context.Context, handler bindings.Handler) er &corev1.Event{}, k.metadata.ResyncPeriod, cache.ResourceEventHandlerFuncs{ - AddFunc: func(obj any) { + AddFunc: func(obj interface{}) { if obj != nil { resultChan <- EventResponse{ Event: "add", @@ -132,7 +132,7 @@ func (k *kubernetesInput) Read(ctx context.Context, handler bindings.Handler) er k.logger.Warnf("Nil Object in Add handle %v", obj) } }, - DeleteFunc: func(obj any) { + DeleteFunc: func(obj interface{}) { if obj != nil { resultChan <- EventResponse{ Event: "delete", @@ -143,7 +143,7 @@ func (k *kubernetesInput) Read(ctx context.Context, handler bindings.Handler) er k.logger.Warnf("Nil Object in Delete handle %v", obj) } }, - UpdateFunc: func(oldObj, newObj any) { + UpdateFunc: func(oldObj, newObj interface{}) { if oldObj != nil && newObj != nil { resultChan <- EventResponse{ Event: "update", diff --git a/bindings/kubernetes/metadata.yaml b/bindings/kubernetes/metadata.yaml deleted file mode 100644 index afe37a03dc..0000000000 --- a/bindings/kubernetes/metadata.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: kubernetes -version: v1 -status: alpha -title: "Kubernetes Events" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/kubernetes-binding/ -binding: - output: false - input: true - operations: - - name: read - description: "Read Kubernetes events" -metadata: - - name: namespace - required: false - description: "The Kubernetes namespace" - example: '"default"' - default: '"default"' - - name: kubeconfigPath - required: false - description: "The path to the kubeconfig file" - example: '"~/.kube/config"' - default: '"~/.kube/config"' - - name: resyncPeriod - required: false - type: duration - description: "The resync period in seconds" - example: '30s' - default: '10s' \ No newline at end of file diff --git a/bindings/localstorage/metadata.yaml b/bindings/localstorage/metadata.yaml deleted file mode 100644 index 52ec52b02d..0000000000 --- a/bindings/localstorage/metadata.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: localstorage -version: v1 -status: stable -title: "Local Storage" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/localstorage/ -binding: - output: true - input: false - operations: - - name: create - description: "Write file to local storage" -metadata: - - name: rootPath - required: true - description: "The root directory path" - example: '"/tmp/dapr"' - - name: fileName - required: true - description: "The file name to write" - example: '"data.txt"' \ No newline at end of file diff --git a/bindings/mqtt3/metadata.go b/bindings/mqtt3/metadata.go index 5bc38066f1..b9fd05abe2 100644 --- a/bindings/mqtt3/metadata.go +++ b/bindings/mqtt3/metadata.go @@ -25,14 +25,16 @@ import ( const ( // Keys. - mqttURL = "url" - mqttTopic = "topic" - mqttQOS = "qos" // This key is deprecated - mqttRetain = "retain" - mqttCleanSession = "cleanSession" - mqttCACert = "caCert" - mqttClientCert = "clientCert" - mqttClientKey = "clientKey" + mqttURL = "url" + mqttTopic = "topic" + mqttQOS = "qos" // This key is deprecated + mqttRetain = "retain" + mqttClientID = "consumerID" + mqttCleanSession = "cleanSession" + mqttCACert = "caCert" + mqttClientCert = "clientCert" + mqttClientKey = "clientKey" + mqttBackOffMaxRetries = "backOffMaxRetries" // Defaults. defaultQOS = 1 diff --git a/bindings/mqtt3/metadata.yaml b/bindings/mqtt3/metadata.yaml deleted file mode 100644 index 9c14c3b13d..0000000000 --- a/bindings/mqtt3/metadata.yaml +++ /dev/null @@ -1,64 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: mqtt3 -version: v1 -status: beta -title: "MQTT v3" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/mqtt3/ -binding: - output: true - input: true - operations: - - name: create - description: "Publish message to MQTT topic" - - name: read - description: "Subscribe to MQTT topic" -authenticationProfiles: - - title: "TLS Authentication" - description: | - Authenticate using TLS certificates. - metadata: - - name: caCert - required: true - description: "CA certificate for TLS" - example: '"-----BEGIN CERTIFICATE-----\n..."' - - name: clientCert - required: true - description: "Client certificate for TLS" - example: '"-----BEGIN CERTIFICATE-----\n..."' - - name: clientKey - required: true - sensitive: true - description: "Client private key for TLS" - example: '"-----BEGIN PRIVATE KEY-----\n..."' -metadata: - - name: url - required: true - description: "The MQTT broker URL" - example: '"tcp://localhost:1883"' - - name: topic - required: true - description: "The MQTT topic" - example: '"my-topic"' - - name: consumerID - required: false - description: "The MQTT client ID" - example: '"my-client"' - - name: retain - required: false - description: "Whether to retain messages" - example: 'false' - default: 'false' - - name: cleanSession - required: false - description: "Whether to use clean session" - example: 'true' - default: 'true' - - name: backOffMaxRetries - required: false - description: "Maximum retries for backoff" - example: '3' - default: '3' \ No newline at end of file diff --git a/bindings/postmark/metadata.yaml b/bindings/postmark/metadata.yaml deleted file mode 100644 index 9670d8b783..0000000000 --- a/bindings/postmark/metadata.yaml +++ /dev/null @@ -1,45 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: postmark -version: v1 -status: alpha -title: "Postmark" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/postmark/ -binding: - output: true - input: false - operations: - - name: create - description: "Send an email using Postmark" -metadata: - - name: serverToken - required: true - description: "The Postmark server token" - example: '"your-server-token"' - - name: accountToken - required: true - description: "The Postmark account token" - example: '"your-account-token"' - - name: emailFrom - required: false - description: "The sender email address" - example: '"sender@example.com"' - - name: emailTo - required: false - description: "The recipient email address" - example: '"recipient@example.com"' - - name: subject - required: false - description: "The email subject" - example: '"Hello from Dapr"' - - name: emailCc - required: false - description: "The CC email address" - example: '"cc@example.com"' - - name: emailBcc - required: false - description: "The BCC email address" - example: '"bcc@example.com"' \ No newline at end of file diff --git a/bindings/redis/metadata.yaml b/bindings/redis/metadata.yaml index ad2bd7c62c..efc3407501 100644 --- a/bindings/redis/metadata.yaml +++ b/bindings/redis/metadata.yaml @@ -42,30 +42,6 @@ authenticationProfiles: secret reference example: "KeFg23!" default: "" - - name: sentinelUsername - type: string - required: false - description: | - Username for Redis Sentinel. Applicable only when "failover" is true, and - Redis Sentinel has authentication enabled. Defaults to empty. - example: "my-sentinel-username" - default: "" - url: - title: "Redis Sentinel authentication documentation" - url: "https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/#configuring-sentinel-instances-with-authentication" - - name: sentinelPassword - type: string - required: false - sensitive: true - description: | - Password for Redis Sentinel. Applicable only when "failover" is true, and - Redis Sentinel has authentication enabled. Use secretKeyRef for - secret reference. Defaults to empty. - example: "KeFg23!" - default: "" - url: - title: "Redis Sentinel authentication documentation" - url: "https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/#configuring-sentinel-instances-with-authentication" metadata: - name: redisHost required: true @@ -128,7 +104,7 @@ metadata: required: false description: | The Redis sentinel master name. Required when "failover" is enabled. - example: "mymaster" + example: "127.0.0.1:6379" url: title: "Redis Sentinel documentation" url: "https://redis.io/docs/manual/sentinel/" diff --git a/bindings/rethinkdb/statechange/metadata.yaml b/bindings/rethinkdb/statechange/metadata.yaml deleted file mode 100644 index 68a72cd378..0000000000 --- a/bindings/rethinkdb/statechange/metadata.yaml +++ /dev/null @@ -1,158 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: rethinkdb.statechange -version: v1 -status: beta -title: "RethinkDB State Change" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/rethinkdb/ -binding: - output: false - input: true - operations: - - name: read - description: "Listen for state changes in RethinkDB" -authenticationProfiles: - - title: "Basic Authentication" - description: "Authenticate using username and password." - metadata: - - name: address - type: string - required: false - description: The RethinkDB server address. - example: "localhost:28015" - - name: addresses - type: string - required: false - description: Comma-separated list of RethinkDB server addresses. - example: "localhost:28015,localhost:28016" - - name: database - type: string - required: true - description: The RethinkDB database name. - example: "dapr" - default: "" - - name: username - type: string - required: false - description: The username for authentication. If not provided, the admin user is used for v1 handshake protocol. - example: "admin" - - name: password - type: string - required: false - description: The password for authentication. This is only used for v1 handshake protocol. - example: "password" - - title: "TLS Authentication" - description: "Authenticate using client certificate and key." - metadata: - - name: enableTLS - type: bool - required: false - description: Whether to enable TLS encryption. - example: false - default: false - - name: clientCert - type: string - required: true - description: The client certificate for TLS authentication. - example: "-----BEGIN CERTIFICATE-----\nXXX..." - - name: clientKey - type: string - required: true - description: The client key for TLS authentication. - example: "-----BEGIN PRIVATE KEY-----\nXXX..." - sensitive: true -metadata: - - name: table - type: string - required: false - description: The table name to store state data. - example: "daprstate" - default: "daprstate" - - name: archive - type: bool - required: false - description: Whether to archive changes to a separate table. - example: false - default: false - - name: timeout - type: string - required: false - description: Connection timeout duration. - example: "10s" - - name: useJSONNumber - type: bool - required: false - description: Whether to use json.Number instead of float64. - example: false - default: false - - name: numRetries - type: number - required: false - description: Number of times to retry queries on connection errors. - example: 3 - - name: hostDecayDuration - type: string - required: false - description: Host decay duration for weighted host selection. - example: "5m" - default: "5m" - - name: useOpentracing - type: bool - required: false - description: Whether to enable opentracing for queries. - example: false - default: false - - name: writeTimeout - type: string - required: false - description: Write timeout duration." - example: "10s" - - name: readTimeout - type: string - required: false - description: Read timeout duration." - example: "10s" - - name: handshakeVersion - type: number - required: false - description: Handshake version for RethinkDB." - example: 1 - - name: keepAlivePeriod - type: string - required: false - description: Keep alive period duration." - example: "30s" - - name: maxIdle - type: number - required: false - description: Maximum number of idle connections." - example: 5 - - name: authKey - type: string - required: false - description: The authentication key for RethinkDB. This field is now deprecated." - example: "auth-key" - sensitive: true - - name: initialCap - type: number - required: false - description: Initial connection pool capacity." - example: 5 - - name: maxOpen - type: number - required: false - description: Maximum number of open connections." - example: 10 - - name: discoverHosts - type: bool - required: false - description: Whether to discover hosts." - example: false - - name: nodeRefreshInterval - type: string - required: false - description: Node refresh interval duration." - example: "5m" \ No newline at end of file diff --git a/bindings/rethinkdb/statechange/statechange.go b/bindings/rethinkdb/statechange/statechange.go index 74e7bbba29..8f53c9cc52 100644 --- a/bindings/rethinkdb/statechange/statechange.go +++ b/bindings/rethinkdb/statechange/statechange.go @@ -15,7 +15,6 @@ package statechange import ( "context" - "crypto/tls" "encoding/json" "errors" "fmt" @@ -23,7 +22,6 @@ import ( "strings" "sync" "sync/atomic" - "time" r "github.com/dancannon/gorethink" @@ -46,37 +44,8 @@ type Binding struct { // StateConfig is the binding config. type StateConfig struct { - ConnectOptsWrapper `mapstructure:",squash"` - Table string `mapstructure:"table"` -} - -// ConnectOptsWrapper wraps r.ConnectOpts but excludes TLSConfig -// This is needed because the metadata decoder does not support nested structs with tags as inputs in the metadata.yaml file -type ConnectOptsWrapper struct { - Address string `gorethink:"address,omitempty"` - Addresses []string `gorethink:"addresses,omitempty"` - Database string `gorethink:"database,omitempty"` - Username string `gorethink:"username,omitempty"` - Password string `gorethink:"password,omitempty"` - AuthKey string `gorethink:"authkey,omitempty"` - Timeout time.Duration `gorethink:"timeout,omitempty"` - WriteTimeout time.Duration `gorethink:"write_timeout,omitempty"` - ReadTimeout time.Duration `gorethink:"read_timeout,omitempty"` - KeepAlivePeriod time.Duration `gorethink:"keep_alive_timeout,omitempty"` - HandshakeVersion int `gorethink:"handshake_version,omitempty"` - MaxIdle int `gorethink:"max_idle,omitempty"` - InitialCap int `gorethink:"initial_cap,omitempty"` - MaxOpen int `gorethink:"max_open,omitempty"` - DiscoverHosts bool `gorethink:"discover_hosts,omitempty"` - NodeRefreshInterval time.Duration `gorethink:"node_refresh_interval,omitempty"` - UseJSONNumber bool `gorethink:"use_json_number,omitempty"` - NumRetries int `gorethink:"num_retries,omitempty"` - HostDecayDuration time.Duration `gorethink:"host_decay_duration,omitempty"` - UseOpentracing bool `gorethink:"use_opentracing,omitempty"` - // TLS fields must be brought in as separate fields as they will not be processed by the metadata decoder properly without this - EnableTLS bool `gorethink:"enable_tls,omitempty"` - ClientCert string `gorethink:"client_cert,omitempty"` - ClientKey string `gorethink:"client_key,omitempty"` + r.ConnectOpts `mapstructure:",squash"` + Table string `mapstructure:"table"` } // NewRethinkDBStateChangeBinding returns a new RethinkDB actor event input binding. @@ -95,40 +64,7 @@ func (b *Binding) Init(ctx context.Context, metadata bindings.Metadata) error { } b.config = cfg - // Convert wrapper to r.ConnectOpts - connectOpts := r.ConnectOpts{ - Address: cfg.Address, - Addresses: cfg.Addresses, - Database: cfg.Database, - Username: cfg.Username, - Password: cfg.Password, - AuthKey: cfg.AuthKey, - Timeout: cfg.Timeout, - WriteTimeout: cfg.WriteTimeout, - ReadTimeout: cfg.ReadTimeout, - KeepAlivePeriod: cfg.KeepAlivePeriod, - HandshakeVersion: r.HandshakeVersion(cfg.HandshakeVersion), - MaxIdle: cfg.MaxIdle, - InitialCap: cfg.InitialCap, - MaxOpen: cfg.MaxOpen, - DiscoverHosts: cfg.DiscoverHosts, - NodeRefreshInterval: cfg.NodeRefreshInterval, - UseJSONNumber: cfg.UseJSONNumber, - NumRetries: cfg.NumRetries, - HostDecayDuration: cfg.HostDecayDuration, - UseOpentracing: cfg.UseOpentracing, - } - - // Configure TLS if enabled - if cfg.EnableTLS { - tlsConfig, tlsErr := createTLSConfig(cfg.ClientCert, cfg.ClientKey) - if tlsErr != nil { - return fmt.Errorf("error creating TLS config: %w", tlsErr) - } - connectOpts.TLSConfig = tlsConfig - } - - ses, err := r.Connect(connectOpts) + ses, err := r.Connect(b.config.ConnectOpts) if err != nil { return fmt.Errorf("error connecting to the database: %w", err) } @@ -137,23 +73,6 @@ func (b *Binding) Init(ctx context.Context, metadata bindings.Metadata) error { return nil } -// createTLSConfig creates a tls.Config from client certificate and key -func createTLSConfig(clientCert, clientKey string) (*tls.Config, error) { - if clientCert == "" || clientKey == "" { - return nil, errors.New("both client certificate and key are required for TLS") - } - - cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey)) - if err != nil { - return nil, fmt.Errorf("error parsing client certificate and key: %w", err) - } - - return &tls.Config{ - Certificates: []tls.Certificate{cert}, - MinVersion: tls.VersionTLS12, - }, nil -} - // Read triggers the RethinkDB scheduler. func (b *Binding) Read(ctx context.Context, handler bindings.Handler) error { if b.closed.Load() { @@ -188,7 +107,7 @@ func (b *Binding) Read(ctx context.Context, handler bindings.Handler) error { go func() { defer b.wg.Done() for readCtx.Err() == nil { - var change any + var change interface{} ok := cursor.Next(&change) if !ok { b.logger.Errorf("error detecting change: %v", cursor.Err()) @@ -230,7 +149,7 @@ func (b *Binding) Close() error { return b.session.Close() } -func metadataToConfig(cfg map[string]string, _ logger.Logger) (StateConfig, error) { +func metadataToConfig(cfg map[string]string, logger logger.Logger) (StateConfig, error) { c := StateConfig{} // prepare metadata keys for decoding diff --git a/bindings/rocketmq/metadata.yaml b/bindings/rocketmq/metadata.yaml deleted file mode 100644 index f290e8fbd2..0000000000 --- a/bindings/rocketmq/metadata.yaml +++ /dev/null @@ -1,77 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: rocketmq -version: v1 -status: alpha -title: "Apache RocketMQ" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/ -binding: - output: true - input: true - operations: - - name: create - description: "Send message to RocketMQ topic" - - name: read - description: "Receive messages from RocketMQ topic" -authenticationProfiles: - - title: "Access Key Authentication" - description: | - Authenticate with RocketMQ using access key and secret. - metadata: - - name: accessKey - required: true - description: "The access key for authentication" - example: '"your-access-key"' - - name: secretKey - required: true - sensitive: true - description: "The secret key for authentication" - example: '"your-secret-key"' -metadata: - - name: accessProto - required: false - description: "SDK protocol" - example: '"tcp"' - allowedValues: - - "tcp" - - "tcp-cgo" - - "http" - - name: nameServer - required: false - description: "The RocketMQ name server address" - example: '"localhost:9876"' - - name: endpoint - required: false - description: "The RocketMQ endpoint (for http proto)" - example: '"http://localhost:8080"' - - name: consumerGroup - required: false - description: "Consumer group for RocketMQ subscribers" - example: '"my-consumer-group"' - - name: consumerBatchSize - required: false - description: "Consumer batch size" - example: '10' - - name: consumerThreadNums - required: false - description: "Consumer thread numbers (for tcp-cgo proto)" - example: '4' - - name: instanceId - required: false - description: "RocketMQ namespace" - example: '"my-instance"' - - name: nameServerDomain - required: false - description: "RocketMQ name server domain" - example: '"rocketmq.example.com"' - - name: retries - required: false - description: "Retry times to connect to RocketMQ broker" - example: '3' - - name: topics - required: true - description: "Topics to subscribe (comma-separated for multiple topics)" - example: '"topic1,topic2,topic3"' \ No newline at end of file diff --git a/bindings/rocketmq/settings.go b/bindings/rocketmq/settings.go index a2588da2ae..de3f7932eb 100644 --- a/bindings/rocketmq/settings.go +++ b/bindings/rocketmq/settings.go @@ -62,7 +62,7 @@ type Settings struct { Topics TopicsDelimited `mapstructure:"topics"` } -func (s *Settings) Decode(in any) error { +func (s *Settings) Decode(in interface{}) error { if err := metadata.DecodeMetadata(in, s); err != nil { return fmt.Errorf("decode error: %w", err) } diff --git a/bindings/sftp/metadata.yaml b/bindings/sftp/metadata.yaml deleted file mode 100644 index d7ebce6781..0000000000 --- a/bindings/sftp/metadata.yaml +++ /dev/null @@ -1,80 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: sftp -version: v1 -status: alpha -title: "Secure File Transfer Protocol (SFTP)" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/sftp/ -binding: - output: true - input: false - operations: - - name: create - description: "Upload file via SFTP" - - name: get - description: "Download file from SFTP" - - name: delete - description: "Delete file from SFTP" - - name: list - description: "List files in SFTP directory" -authenticationProfiles: - - title: "Password Authentication" - description: | - Authenticate using username and password. - metadata: - - name: username - required: true - description: "The SFTP username" - example: '"sftpuser"' - - name: password - required: true - sensitive: true - description: "The SFTP password" - example: '"your-password"' - - title: "Private Key Authentication" - description: | - Authenticate using username and private key. - metadata: - - name: username - required: true - description: "The SFTP username" - example: '"sftpuser"' - - name: privateKey - required: true - sensitive: true - description: "The private key for authentication" - example: '"-----BEGIN OPENSSH PRIVATE KEY-----\n..."' - - name: privateKeyPassphrase - required: false - sensitive: true - description: "The passphrase for the private key" - example: '"your-passphrase"' -metadata: - - name: address - required: true - description: "The SFTP server address (host:port)" - example: '"sftp.example.com:22"' - - name: rootPath - required: true - description: "The root directory path on the SFTP server" - example: '"/home/sftpuser"' - - name: fileName - required: false - description: "The file name (can be overridden in request metadata)" - example: '"data.txt"' - - name: hostPublicKey - required: false - description: "The host public key for verification" - example: '"ssh-rsa AAAAB3NzaC1yc2E..."' - - name: knownHostsFile - required: false - description: "Path to the known_hosts file" - example: '"/path/to/known_hosts"' - - name: insecureIgnoreHostKey - required: false - description: "Skip host key verification (insecure)" - example: 'false' - default: 'false' \ No newline at end of file diff --git a/bindings/smtp/metadata.yaml b/bindings/smtp/metadata.yaml deleted file mode 100644 index e821b394e7..0000000000 --- a/bindings/smtp/metadata.yaml +++ /dev/null @@ -1,70 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: smtp -version: v1 -status: alpha -title: "SMTP" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/smtp/ -binding: - output: true - input: false - operations: - - name: create - description: "Send email via SMTP" -authenticationProfiles: - - title: "User/Password Authentication" - description: | - Authenticate with SMTP server using username and password. - metadata: - - name: user - required: true - description: "The SMTP username" - example: '"user@gmail.com"' - - name: password - required: true - sensitive: true - description: "The SMTP password" - example: '"your-password"' -metadata: - - name: host - required: true - description: "The SMTP server host" - example: '"smtp.gmail.com"' - - name: port - required: false - description: "The SMTP server port" - example: '587' - default: '587' - - name: emailFrom - required: true - description: "The sender email address" - example: '"sender@example.com"' - - name: emailTo - required: true - description: "The recipient email address" - example: '"recipient@example.com"' - - name: emailCC - required: false - description: "The email CC address" - example: '"cc@example.com"' - - name: emailBCC - required: false - description: "The email BCC address" - example: '"bcc@example.com"' - - name: priority - required: false - description: "The email priority" - example: '3' - default: '3' - - name: subject - required: true - description: "The email subject" - example: '"Hello from Dapr"' - - name: skipTLSVerify - required: false - description: "Skip TLS verification" - example: 'false' - default: 'false' \ No newline at end of file diff --git a/bindings/twilio/sendgrid/metadata.yaml b/bindings/twilio/sendgrid/metadata.yaml deleted file mode 100644 index d756d47c4a..0000000000 --- a/bindings/twilio/sendgrid/metadata.yaml +++ /dev/null @@ -1,63 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: twilio.sendgrid -version: v1 -status: alpha -title: "Twilio SendGrid" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/sendgrid/ -binding: - output: true - input: false - operations: - - name: create - description: "Send email via SendGrid" -authenticationProfiles: - - title: "API Key Authentication" - description: | - Authenticate using SendGrid API key. - metadata: - - name: apiKey - required: true - sensitive: true - description: "The SendGrid API key" - example: '"SG.your-api-key"' -metadata: - - name: emailFrom - required: true - description: "The sender email address" - example: '"sender@example.com"' - - name: emailTo - required: true - description: "The recipient email address" - example: '"recipient@example.com"' - - name: subject - required: true - description: "The email subject" - example: '"Hello from Dapr"' - - name: emailCc - required: false - description: "The CC email address" - example: '"cc@example.com"' - - name: emailBcc - required: false - description: "The BCC email address" - example: '"bcc@example.com"' - - name: dynamicTemplateData - required: false - description: "The dynamic template data" - example: '"{"name":"John","age":30}"' - - name: dynamicTemplateId - required: false - description: "The dynamic template ID" - example: '"your-template-id"' - - name: emailFromName - required: false - description: "The sender name" - example: '"John Doe"' - - name: emailToName - required: false - description: "The recipient name" - example: '"Jane Smith"' \ No newline at end of file diff --git a/bindings/twilio/sms/metadata.yaml b/bindings/twilio/sms/metadata.yaml deleted file mode 100644 index 61db57040f..0000000000 --- a/bindings/twilio/sms/metadata.yaml +++ /dev/null @@ -1,45 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: twilio.sms -version: v1 -status: stable -title: "Twilio SMS" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/twilio/ -binding: - output: true - input: false - operations: - - name: create - description: "Send SMS via Twilio" -authenticationProfiles: - - title: "Twilio Authentication" - description: | - Authenticate using Twilio account credentials. - metadata: - - name: accountSid - required: true - description: "The Twilio account SID" - example: '"AC1234567890abcdef"' - - name: authToken - required: true - sensitive: true - description: "The Twilio auth token" - example: '"your-auth-token"' -metadata: - - name: fromNumber - required: true - description: "The sender phone number" - example: '"+1234567890"' - - name: toNumber - required: true - description: "The recipient phone number" - example: '"+0987654321"' - - name: timeout - required: false - type: duration - description: "The timeout for the SMS request" - example: '30s' - default: '30s' \ No newline at end of file diff --git a/bindings/twilio/sms/sms.go b/bindings/twilio/sms/sms.go index 42503d4ce3..2bd4a59609 100644 --- a/bindings/twilio/sms/sms.go +++ b/bindings/twilio/sms/sms.go @@ -33,6 +33,10 @@ import ( const ( toNumber = "toNumber" + fromNumber = "fromNumber" + accountSid = "accountSid" + authToken = "authToken" + timeout = "timeout" twilioURLBase = "https://api.twilio.com/2010-04-01/Accounts/" ) diff --git a/bindings/wasm/metadata.yaml b/bindings/wasm/metadata.yaml deleted file mode 100644 index b2ad1b025c..0000000000 --- a/bindings/wasm/metadata.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: bindings -name: wasm -version: v1 -status: alpha -title: "WebAssembly (WASM)" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-bindings/wasm/ -binding: - output: true - input: false - operations: - - name: create - description: "Execute WASM function" -metadata: - - name: strictSandbox - required: false - description: "Strict sandbox mode. When true, uses fake sources to avoid vulnerabilities such as timing attacks." - example: 'true' - default: 'false' - - name: url - required: true - description: "The URL of the WASM file" - example: '"https://example.com/function.wasm"' \ No newline at end of file From 6f1ca96be1d7bd548c1386b49922fecd72a8e04c Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Mon, 11 Aug 2025 09:59:36 -0500 Subject: [PATCH 25/32] revert: checkout nameresolution dir from main since in diff pr now Signed-off-by: Samantha Coyle --- .../{hashicorp => }/consul/README.md | 0 .../{hashicorp => }/consul/configuration.go | 6 +- .../{hashicorp => }/consul/consul.go | 0 .../{hashicorp => }/consul/consul_test.go | 0 .../{hashicorp => }/consul/watcher.go | 0 nameresolution/hashicorp/consul/metadata.yaml | 157 ------------------ nameresolution/kubernetes/kubernetes.go | 2 +- nameresolution/kubernetes/kubernetes_test.go | 6 +- nameresolution/kubernetes/metadata.yaml | 11 -- nameresolution/mdns/metadata.yaml | 11 -- nameresolution/sqlite/metadata.yaml | 11 -- 11 files changed, 7 insertions(+), 197 deletions(-) rename nameresolution/{hashicorp => }/consul/README.md (100%) rename nameresolution/{hashicorp => }/consul/configuration.go (98%) rename nameresolution/{hashicorp => }/consul/consul.go (100%) rename nameresolution/{hashicorp => }/consul/consul_test.go (100%) rename nameresolution/{hashicorp => }/consul/watcher.go (100%) delete mode 100644 nameresolution/hashicorp/consul/metadata.yaml delete mode 100644 nameresolution/kubernetes/metadata.yaml delete mode 100644 nameresolution/mdns/metadata.yaml delete mode 100644 nameresolution/sqlite/metadata.yaml diff --git a/nameresolution/hashicorp/consul/README.md b/nameresolution/consul/README.md similarity index 100% rename from nameresolution/hashicorp/consul/README.md rename to nameresolution/consul/README.md diff --git a/nameresolution/hashicorp/consul/configuration.go b/nameresolution/consul/configuration.go similarity index 98% rename from nameresolution/hashicorp/consul/configuration.go rename to nameresolution/consul/configuration.go index 017894c114..ccf4e58010 100644 --- a/nameresolution/hashicorp/consul/configuration.go +++ b/nameresolution/consul/configuration.go @@ -30,7 +30,7 @@ const defaultDaprPortMetaKey string = "DAPR_PORT" // default key for DaprPort in // that way breaking changes in future versions of the consul api cannot break user configuration. type intermediateConfig struct { Client *Config - Checks []*AgentServiceCheck // TODO: update this to bring properly to metadata + Checks []*AgentServiceCheck Tags []string Meta map[string]string QueryOptions *QueryOptions @@ -43,7 +43,7 @@ type intermediateConfig struct { type configSpec struct { Client *consul.Config - Checks []*consul.AgentServiceCheck // TODO: update this to bring properly to metadata + Checks []*consul.AgentServiceCheck Tags []string Meta map[string]string QueryOptions *consul.QueryOptions @@ -60,7 +60,7 @@ func newIntermediateConfig() intermediateConfig { } } -func parseConfig(rawConfig any) (configSpec, error) { +func parseConfig(rawConfig interface{}) (configSpec, error) { var result configSpec rawConfig, err := config.Normalize(rawConfig) if err != nil { diff --git a/nameresolution/hashicorp/consul/consul.go b/nameresolution/consul/consul.go similarity index 100% rename from nameresolution/hashicorp/consul/consul.go rename to nameresolution/consul/consul.go diff --git a/nameresolution/hashicorp/consul/consul_test.go b/nameresolution/consul/consul_test.go similarity index 100% rename from nameresolution/hashicorp/consul/consul_test.go rename to nameresolution/consul/consul_test.go diff --git a/nameresolution/hashicorp/consul/watcher.go b/nameresolution/consul/watcher.go similarity index 100% rename from nameresolution/hashicorp/consul/watcher.go rename to nameresolution/consul/watcher.go diff --git a/nameresolution/hashicorp/consul/metadata.yaml b/nameresolution/hashicorp/consul/metadata.yaml deleted file mode 100644 index 40b58a2451..0000000000 --- a/nameresolution/hashicorp/consul/metadata.yaml +++ /dev/null @@ -1,157 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: nameresolution -name: hashicorp.consul -version: v1 -status: alpha -title: "HashiCorp Consul" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-name-resolution/setup-nr-consul/ -authenticationProfiles: - - title: "Token Authentication" - description: "Connect to Consul using a token for authentication." - metadata: - - name: token - type: string - required: true - description: The Consul ACL token for authentication. - sensitive: true - example: "your-consul-token" - - title: "Username/Password Authentication" - description: "Connect to Consul using a username and password." - metadata: - - name: username - type: string - required: false - description: Username for HTTP basic authentication. - example: "username" - - name: password - type: string - required: false - description: Password for HTTP basic authentication. - sensitive: true - example: "password" - - title: "TLS Authentication" - description: "Connect to Consul using TLS encryption with certificates." - metadata: - - name: tlsAddress - type: string - required: true - description: The TLS address of the Consul server. - example: "localhost:8501" - - name: caFile - type: string - required: true - description: Path to the CA certificate file. - example: "/path/to/ca.crt" - - name: certFile - type: string - required: true - description: Path to the client certificate file. - example: "/path/to/client.crt" - - name: keyFile - type: string - required: true - description: Path to the client private key file. - example: "/path/to/client.key" - - name: insecureSkipVerify - type: bool - required: false - description: Skip TLS certificate verification. - example: false - default: false -metadata: - - name: address - type: string - required: true - description: The address of the Consul server. - example: "localhost:8500" - - name: scheme - type: string - required: false - description: The scheme to use for connecting to Consul (http or https). - example: "https" - default: "http" - - name: datacenter - type: string - required: false - description: The Consul datacenter to use. - example: "dc1" - - name: waitTime - type: string - required: false - description: The wait time for Consul operations. - example: "10s" - - name: tokenFile - type: string - required: false - description: Path to a file containing the Consul ACL token. - example: "/path/to/token" - - name: daprPortMetaKey - type: string - required: false - description: The metadata key used to store the Dapr port. - example: "DAPR_PORT" - default: "DAPR_PORT" - - name: selfRegister - type: bool - required: false - description: Whether to register this service with Consul. - example: true - default: false - - name: selfDeregister - type: bool - required: false - description: Whether to deregister this service from Consul on shutdown. - example: true - default: false - - name: useCache - type: bool - required: false - description: Whether to use caching for service lookups. - example: true - default: false - - name: tags - type: string - required: false - description: Tags to associate with the service registration. - example: "dapr,v1" - - name: namespace - type: string - required: false - description: The Consul namespace to use for queries. - example: "default" - - name: partition - type: string - required: false - description: The Consul partition to use for queries. - example: "default" - - name: queryDatacenter - type: string - required: false - description: The Consul datacenter to use for queries. - example: "dc1" - - name: queryToken - type: string - required: false - description: The Consul ACL token to use for queries. - sensitive: true - example: "query-token" - - name: queryWaitTime - type: string - required: false - description: The wait time for Consul queries. - example: "5s" - - name: allowStale - type: bool - required: false - description: Whether to allow stale results in queries. - example: true - default: false - - name: requireConsistent - type: bool - required: false - description: Whether to require consistent reads in queries. - example: false - default: false diff --git a/nameresolution/kubernetes/kubernetes.go b/nameresolution/kubernetes/kubernetes.go index 14ff0cb255..f8233ef72c 100644 --- a/nameresolution/kubernetes/kubernetes.go +++ b/nameresolution/kubernetes/kubernetes.go @@ -69,7 +69,7 @@ func (k *resolver) Init(ctx context.Context, metadata nameresolution.Metadata) e return err } - if cfg, ok := configInterface.(map[string]any); ok { + if cfg, ok := configInterface.(map[string]interface{}); ok { clusterDomainAny := cfg[ClusterDomainKey] tmplStrAny := cfg[TemplateKey] diff --git a/nameresolution/kubernetes/kubernetes_test.go b/nameresolution/kubernetes/kubernetes_test.go index 7eec4a7ec9..d31b175999 100644 --- a/nameresolution/kubernetes/kubernetes_test.go +++ b/nameresolution/kubernetes/kubernetes_test.go @@ -37,7 +37,7 @@ func TestResolve(t *testing.T) { func TestResolveWithCustomClusterDomain(t *testing.T) { resolver := NewResolver(logger.NewLogger("test")) _ = resolver.Init(t.Context(), nameresolution.Metadata{ - Configuration: map[string]any{ + Configuration: map[string]interface{}{ "clusterDomain": "mydomain.com", }, }) @@ -53,7 +53,7 @@ func TestResolveWithCustomClusterDomain(t *testing.T) { func TestResolveWithTemplate(t *testing.T) { resolver := NewResolver(logger.NewLogger("test")) _ = resolver.Init(t.Context(), nameresolution.Metadata{ - Configuration: map[string]any{ + Configuration: map[string]interface{}{ "template": "{{.ID}}-{{.Namespace}}.internal:{{.Port}}", }, }) @@ -69,7 +69,7 @@ func TestResolveWithTemplate(t *testing.T) { func TestResolveWithTemplateAndData(t *testing.T) { resolver := NewResolver(logger.NewLogger("test")) _ = resolver.Init(t.Context(), nameresolution.Metadata{ - Configuration: map[string]any{ + Configuration: map[string]interface{}{ "template": "{{.ID}}-{{.Data.region}}.internal:{{.Port}}", }, }) diff --git a/nameresolution/kubernetes/metadata.yaml b/nameresolution/kubernetes/metadata.yaml deleted file mode 100644 index 3f7ae02834..0000000000 --- a/nameresolution/kubernetes/metadata.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: nameresolution -name: kubernetes -version: v1 -status: stable -title: "Kubernetes" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-name-resolution/nr-kubernetes/ -metadata: [] \ No newline at end of file diff --git a/nameresolution/mdns/metadata.yaml b/nameresolution/mdns/metadata.yaml deleted file mode 100644 index 54374bbf38..0000000000 --- a/nameresolution/mdns/metadata.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: nameresolution -name: mdns -version: v1 -status: stable -title: "mDNS" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-name-resolution/nr-mdns/ -metadata: [] \ No newline at end of file diff --git a/nameresolution/sqlite/metadata.yaml b/nameresolution/sqlite/metadata.yaml deleted file mode 100644 index 0d59bec957..0000000000 --- a/nameresolution/sqlite/metadata.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: nameresolution -name: sqlite -version: v1 -status: alpha -title: "SQLite" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-name-resolution/nr-sqlite/ -metadata: [] From e4da01b679cfd30c8dd1a1b3e32dcedc2ae78eed Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Mon, 11 Aug 2025 10:09:15 -0500 Subject: [PATCH 26/32] revert: checkout middleware dir from main since in diff pr now Signed-off-by: Samantha Coyle --- middleware/http/bearer/metadata.yaml | 29 ----- middleware/http/oauth2/metadata.yaml | 66 ---------- middleware/http/oauth2/oauth2_middleware.go | 22 ---- .../http/oauth2/oauth2_middleware_test.go | 49 -------- .../oauth2clientcredentials/metadata.yaml | 64 ---------- .../oauth2clientcredentials_middleware.go | 53 +++----- ...ntcredentials_middleware_benchmark_test.go | 113 ------------------ ...oauth2clientcredentials_middleware_test.go | 107 +---------------- middleware/http/opa/metadata.yaml | 34 ------ middleware/http/opa/middleware.go | 1 - middleware/http/ratelimit/metadata.yaml | 19 --- ...ias_alias.go => routeralias_middleware.go} | 0 ...test.go => routeralias_middleware_test.go} | 0 middleware/http/routerchecker/metadata.yaml | 18 --- middleware/http/sentinel/metadata.yaml | 90 -------------- middleware/http/wasm/metadata.yaml | 27 ----- 16 files changed, 17 insertions(+), 675 deletions(-) delete mode 100644 middleware/http/bearer/metadata.yaml delete mode 100644 middleware/http/oauth2/metadata.yaml delete mode 100644 middleware/http/oauth2clientcredentials/metadata.yaml delete mode 100644 middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_benchmark_test.go delete mode 100644 middleware/http/opa/metadata.yaml delete mode 100644 middleware/http/ratelimit/metadata.yaml rename middleware/http/routeralias/{routeralias_alias.go => routeralias_middleware.go} (100%) rename middleware/http/routeralias/{routeralias_alias_test.go => routeralias_middleware_test.go} (100%) delete mode 100644 middleware/http/routerchecker/metadata.yaml delete mode 100644 middleware/http/sentinel/metadata.yaml delete mode 100644 middleware/http/wasm/metadata.yaml diff --git a/middleware/http/bearer/metadata.yaml b/middleware/http/bearer/metadata.yaml deleted file mode 100644 index 1e0ee4e45d..0000000000 --- a/middleware/http/bearer/metadata.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: middleware -name: bearer -version: v1 -status: stable -title: "Bearer Token Authentication" -description: | - The Bearer middleware provides JWT token authentication for HTTP requests. - It validates Bearer tokens in the Authorization header and can extract claims for downstream processing. -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-bearer/ -metadata: - - name: jwksURL - type: string - required: true - description: "The URL of the JSON Web Key Set (JWKS) endpoint" - example: "https://accounts.google.com/.well-known/jwks.json" - - name: issuer - type: string - required: true - description: "The expected issuer of the JWT tokens" - example: "https://accounts.google.com" - - name: audience - type: string - required: true - description: "The expected audience of the JWT tokens" - example: "my-app" diff --git a/middleware/http/oauth2/metadata.yaml b/middleware/http/oauth2/metadata.yaml deleted file mode 100644 index b6aff861be..0000000000 --- a/middleware/http/oauth2/metadata.yaml +++ /dev/null @@ -1,66 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: middleware -name: oauth2 -version: v1 -status: alpha -title: "OAuth2 Authentication" -description: | - The OAuth2 middleware provides OAuth2 authentication for HTTP requests. - It handles OAuth2 flows and token validation for securing API endpoints. -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-oauth2/ -authenticationProfiles: - - title: "OAuth2 Authentication" - description: "Configure OAuth2 authentication with any OAuth2 provider" - metadata: - - name: clientID - type: string - required: true - description: "The OAuth2 client ID from your OAuth2 provider" - example: "your-client-id" - - name: clientSecret - type: string - required: true - description: "The OAuth2 client secret from your OAuth2 provider" - sensitive: true - example: "your-client-secret" - - name: authURL - type: string - required: true - description: "The OAuth2 authorization URL from your provider" - example: "https://accounts.google.com/o/oauth2/v2/auth" - - name: tokenURL - type: string - required: true - description: "The OAuth2 token URL from your provider" - example: "https://oauth2.googleapis.com/token" - - name: scopes - type: string - required: false - description: "OAuth2 scopes to request from your provider" - example: "openid profile email" -metadata: - - name: redirectURL - type: string - required: false - description: "The OAuth2 redirect URL for your application" - example: "http://localhost:8080/callback" - - name: authHeaderName - type: string - required: false - description: "The name of the authorization header to use" - example: "Authorization" - default: "Authorization" - - name: forceHTTPS - type: string - required: false - description: "Whether to force HTTPS for the redirect URL" - example: "true" - default: "false" - - name: pathFilter - type: string - required: false - description: "Regular expression to filter which paths require authentication" - example: "^/api/.*" diff --git a/middleware/http/oauth2/oauth2_middleware.go b/middleware/http/oauth2/oauth2_middleware.go index 2211caa58b..4c7b4b6629 100644 --- a/middleware/http/oauth2/oauth2_middleware.go +++ b/middleware/http/oauth2/oauth2_middleware.go @@ -18,7 +18,6 @@ import ( "net/http" "net/url" "reflect" - "regexp" "strings" "github.com/fasthttp-contrib/sessions" @@ -43,9 +42,6 @@ type oAuth2MiddlewareMetadata struct { AuthHeaderName string `json:"authHeaderName" mapstructure:"authHeaderName"` RedirectURL string `json:"redirectURL" mapstructure:"redirectURL"` ForceHTTPS string `json:"forceHTTPS" mapstructure:"forceHTTPS"` - PathFilter string `json:"pathFilter" mapstructure:"pathFilter"` - - pathFilterRegex *regexp.Regexp } // NewOAuth2Middleware returns a new oAuth2 middleware. @@ -88,15 +84,6 @@ func (m *Middleware) GetHandler(ctx context.Context, metadata middleware.Metadat return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if meta.pathFilterRegex != nil { - matched := meta.pathFilterRegex.MatchString(r.URL.Path) - if !matched { - m.logger.Debugf("PathFilter %s didn't match %s! Skipping!", meta.PathFilter, r.URL.Path) - next.ServeHTTP(w, r) - return - } - } - session := sessions.Start(w, r) if session.GetString(meta.AuthHeaderName) != "" { @@ -166,15 +153,6 @@ func (m *Middleware) getNativeMetadata(metadata middleware.Metadata) (*oAuth2Mid if err != nil { return nil, err } - - if middlewareMetadata.PathFilter != "" { - rx, err := regexp.Compile(middlewareMetadata.PathFilter) - if err != nil { - return nil, err - } - middlewareMetadata.pathFilterRegex = rx - } - return &middlewareMetadata, nil } diff --git a/middleware/http/oauth2/oauth2_middleware_test.go b/middleware/http/oauth2/oauth2_middleware_test.go index e8f67e4183..4f12a5825c 100644 --- a/middleware/http/oauth2/oauth2_middleware_test.go +++ b/middleware/http/oauth2/oauth2_middleware_test.go @@ -61,52 +61,3 @@ func TestOAuth2CreatesAuthorizationHeaderWhenInSessionState(t *testing.T) { assert.Equal(t, "Bearer abcd", r.Header.Get("someHeader")) } - -func TestOAuth2CreatesAuthorizationHeaderGetNativeMetadata(t *testing.T) { - var metadata middleware.Metadata - metadata.Properties = map[string]string{ - "clientID": "testId", - "clientSecret": "testSecret", - "scopes": "ascope", - "authURL": "https://idp:9999", - "tokenURL": "https://idp:9999", - "redirectUrl": "https://localhost:9999", - "authHeaderName": "someHeader", - } - - log := logger.NewLogger("oauth2.test") - oauth2Middleware, ok := NewOAuth2Middleware(log).(*Middleware) - require.True(t, ok) - - tc := []struct { - name string - pathFilter string - wantErr bool - }{ - {name: "empty pathFilter", pathFilter: "", wantErr: false}, - {name: "wildcard pathFilter", pathFilter: ".*", wantErr: false}, - {name: "api path pathFilter", pathFilter: "/api/v1/users", wantErr: false}, - {name: "debug endpoint pathFilter", pathFilter: "^/debug/?$", wantErr: false}, - {name: "user id pathFilter", pathFilter: "^/user/[0-9]+$", wantErr: false}, - {name: "invalid wildcard pathFilter", pathFilter: "*invalid", wantErr: true}, - {name: "unclosed parenthesis pathFilter", pathFilter: "invalid(", wantErr: true}, - {name: "unopened parenthesis pathFilter", pathFilter: "invalid)", wantErr: true}, - } - - for _, tt := range tc { - t.Run(tt.name, func(t *testing.T) { - metadata.Properties["pathFilter"] = tt.pathFilter - nativeMetadata, err := oauth2Middleware.getNativeMetadata(metadata) - if tt.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - if tt.pathFilter != "" { - require.NotNil(t, nativeMetadata.pathFilterRegex) - } else { - require.Nil(t, nativeMetadata.pathFilterRegex) - } - } - }) - } -} diff --git a/middleware/http/oauth2clientcredentials/metadata.yaml b/middleware/http/oauth2clientcredentials/metadata.yaml deleted file mode 100644 index 906ca7836b..0000000000 --- a/middleware/http/oauth2clientcredentials/metadata.yaml +++ /dev/null @@ -1,64 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: middleware -name: oauth2clientcredentials -version: v1 -status: alpha -title: "OAuth2 Client Credentials" -description: | - The OAuth2 Client Credentials middleware provides OAuth2 client credentials flow authentication. - It handles machine-to-machine authentication using client credentials. -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-middleware/oauth2clientcredentials/ -authenticationProfiles: - - title: "OAuth2 Client Credentials" - description: "Configure OAuth2 client credentials authentication with any OAuth2 provider" - metadata: - - name: clientID - type: string - required: true - description: "The client ID of your application that is created as part of a credential hosted by a OAuth-enabled platform" - example: "your-client-id" - - name: clientSecret - type: string - required: true - description: "The client secret of your application that is created as part of a credential hosted by a OAuth-enabled platform" - sensitive: true - example: "your-client-secret" - - name: scopes - type: string - required: false - description: "A list of space-delimited, case-sensitive strings of scopes which are typically used for authorization in the application" - example: "https://www.googleapis.com/auth/userinfo.email" - - name: tokenURL - type: string - required: true - description: "The endpoint is used by the client to obtain an access token by presenting its authorization grant or refresh token" - example: "https://accounts.google.com/o/oauth2/token" -metadata: - - name: pathFilter - type: string - required: false - description: "Regular expression to filter which paths require authentication" - example: "^/api/.*" - - name: headerName - type: string - required: true - description: "The authorization header name to forward to your application" - example: "authorization" - - name: endpointParamsQuery - type: string - required: false - description: "Specifies additional parameters for requests to the token endpoint" - example: "param1=value1¶m2=value2" - - name: authStyle - type: number - required: false - description: "Optionally specifies how the endpoint wants the client ID & client secret sent. 0: Auto-detect (tries both ways and caches the successful way), 1: Sends client_id and client_secret in POST body as application/x-www-form-urlencoded parameters, 2: Sends client_id and client_secret using HTTP Basic Authorization" - example: 0 - default: 0 - allowedValues: - - 0 - - 1 - - 2 \ No newline at end of file diff --git a/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware.go b/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware.go index 12f955d641..484376efe3 100644 --- a/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware.go +++ b/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware.go @@ -21,7 +21,6 @@ import ( "net/http" "net/url" "reflect" - "regexp" "strings" "time" @@ -44,9 +43,6 @@ type oAuth2ClientCredentialsMiddlewareMetadata struct { HeaderName string `json:"headerName" mapstructure:"headerName"` EndpointParamsQuery string `json:"endpointParamsQuery,omitempty" mapstructure:"endpointParamsQuery"` AuthStyle int `json:"authStyle" mapstructure:"authStyle"` - PathFilter string `json:"pathFilter" mapstructure:"pathFilter"` - - pathFilterRegex *regexp.Regexp } // TokenProviderInterface provides a common interface to Mock the Token retrieval in unit tests. @@ -73,7 +69,7 @@ type Middleware struct { tokenProvider TokenProviderInterface } -// GetHandler returns the HTTP handler provided by the middleware. +// GetHandler retruns the HTTP handler provided by the middleware. func (m *Middleware) GetHandler(_ context.Context, metadata middleware.Metadata) (func(next http.Handler) http.Handler, error) { meta, err := m.getNativeMetadata(metadata) if err != nil { @@ -102,38 +98,27 @@ func (m *Middleware) GetHandler(_ context.Context, metadata middleware.Metadata) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var headerValue string - if meta.pathFilterRegex != nil { - matched := meta.pathFilterRegex.MatchString(r.URL.Path) - if !matched { - m.log.Debugf("PathFilter %s didn't match %s! Skipping!", meta.PathFilter, r.URL.Path) - next.ServeHTTP(w, r) + // Check if valid token is in the cache + cachedToken, found := m.tokenCache.Get(cacheKey) + if !found { + m.log.Debugf("Cached token not found, try get one") + + token, err := m.tokenProvider.GetToken(r.Context(), conf) + if err != nil { + m.log.Errorf("Error acquiring token: %s", err) return } - } - // Check if valid token is in the cache - cachedToken, found := m.tokenCache.Get(cacheKey) - if found { + tokenExpirationDuration := time.Until(token.Expiry) + m.log.Debugf("Token expires at %s (%s from now)", token.Expiry, tokenExpirationDuration) + + headerValue = token.Type() + " " + token.AccessToken + m.tokenCache.Set(cacheKey, headerValue, tokenExpirationDuration) + } else { m.log.Debugf("Cached token found for key %s", cacheKey) headerValue = cachedToken.(string) - r.Header.Add(meta.HeaderName, headerValue) - next.ServeHTTP(w, r) - return - } - - m.log.Infof("Cached token not found, attempting to retrieve a new one") - token, err := m.tokenProvider.GetToken(r.Context(), conf) - if err != nil { - m.log.Errorf("Error acquiring token: %s", err) - return } - tokenExpirationDuration := time.Until(token.Expiry) - m.log.Infof("Token expires at %s (%s from now)", token.Expiry, tokenExpirationDuration) - - headerValue = token.Type() + " " + token.AccessToken - m.tokenCache.Set(cacheKey, headerValue, tokenExpirationDuration) - r.Header.Add(meta.HeaderName, headerValue) next.ServeHTTP(w, r) }) @@ -157,14 +142,6 @@ func (m *Middleware) getNativeMetadata(metadata middleware.Metadata) (*oAuth2Cli m.checkMetadataValueExists(&errorString, &middlewareMetadata.Scopes, "scopes") m.checkMetadataValueExists(&errorString, &middlewareMetadata.TokenURL, "tokenURL") - if middlewareMetadata.PathFilter != "" { - rx, err := regexp.Compile(middlewareMetadata.PathFilter) - if err != nil { - errorString += "Parameter 'pathFilter' is not a valid regex: " + err.Error() + ". " - } - middlewareMetadata.pathFilterRegex = rx - } - // Value-check AuthStyle if middlewareMetadata.AuthStyle < 0 || middlewareMetadata.AuthStyle > 2 { errorString += fmt.Sprintf("Parameter 'authStyle' can only have the values 0,1,2. Received: '%d'. ", middlewareMetadata.AuthStyle) diff --git a/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_benchmark_test.go b/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_benchmark_test.go deleted file mode 100644 index 032cbc11d6..0000000000 --- a/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_benchmark_test.go +++ /dev/null @@ -1,113 +0,0 @@ -/* -Copyright 2025 The Dapr Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package oauth2clientcredentials - -import ( - "fmt" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" - "golang.org/x/oauth2" - - "github.com/dapr/components-contrib/middleware" - mock "github.com/dapr/components-contrib/middleware/http/oauth2clientcredentials/mocks" - "github.com/dapr/kit/logger" -) - -func BenchmarkTestOAuth2ClientCredentialsGetHandler(b *testing.B) { - mockCtrl := gomock.NewController(b) - defer mockCtrl.Finish() - mockTokenProvider := mock.NewMockTokenProviderInterface(mockCtrl) - gomock.InOrder( - mockTokenProvider. - EXPECT(). - GetToken(gomock.Any()). - Return(&oauth2.Token{ - AccessToken: "abcd", - TokenType: "Bearer", - Expiry: time.Now().Add(1 * time.Minute), - }, nil). - Times(1), - ) - - var metadata middleware.Metadata - metadata.Properties = map[string]string{ - "clientID": "testId", - "clientSecret": "testSecret", - "scopes": "ascope", - "tokenURL": "https://localhost:9999", - "headerName": "authorization", - "authStyle": "1", - } - - log := logger.NewLogger("oauth2clientcredentials.test") - oauth2clientcredentialsMiddleware, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) - require.True(b, ok) - oauth2clientcredentialsMiddleware.SetTokenProvider(mockTokenProvider) - handler, err := oauth2clientcredentialsMiddleware.GetHandler(b.Context(), metadata) - require.NoError(b, err) - - for i := range b.N { - url := fmt.Sprintf("http://dapr.io/api/v1/users/%d", i) - r := httptest.NewRequest(http.MethodGet, url, nil) - w := httptest.NewRecorder() - handler(http.HandlerFunc(mockedRequestHandler)).ServeHTTP(w, r) - } -} - -func BenchmarkTestOAuth2ClientCredentialsGetHandlerWithPathFilter(b *testing.B) { - mockCtrl := gomock.NewController(b) - defer mockCtrl.Finish() - mockTokenProvider := mock.NewMockTokenProviderInterface(mockCtrl) - gomock.InOrder( - mockTokenProvider. - EXPECT(). - GetToken(gomock.Any()). - Return(&oauth2.Token{ - AccessToken: "abcd", - TokenType: "Bearer", - Expiry: time.Now().Add(1 * time.Minute), - }, nil). - Times(1), - ) - - var metadata middleware.Metadata - metadata.Properties = map[string]string{ - "clientID": "testId", - "clientSecret": "testSecret", - "scopes": "ascope", - "tokenURL": "https://localhost:9999", - "headerName": "authorization", - "authStyle": "1", - "pathFilter": "/api/v1/users/.*", - } - - log := logger.NewLogger("oauth2clientcredentials.test") - oauth2clientcredentialsMiddleware, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) - require.True(b, ok) - oauth2clientcredentialsMiddleware.SetTokenProvider(mockTokenProvider) - handler, err := oauth2clientcredentialsMiddleware.GetHandler(b.Context(), metadata) - require.NoError(b, err) - - for i := range b.N { - url := fmt.Sprintf("http://dapr.io/api/v1/users/%d", i) - r := httptest.NewRequest(http.MethodGet, url, nil) - w := httptest.NewRecorder() - handler(http.HandlerFunc(mockedRequestHandler)).ServeHTTP(w, r) - } -} diff --git a/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_test.go b/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_test.go index 7fdbc86c69..48d27754e4 100644 --- a/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_test.go +++ b/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_test.go @@ -24,7 +24,6 @@ import ( "github.com/stretchr/testify/require" oauth2 "golang.org/x/oauth2" - "github.com/dapr/components-contrib/metadata" "github.com/dapr/components-contrib/middleware" mock "github.com/dapr/components-contrib/middleware/http/oauth2clientcredentials/mocks" "github.com/dapr/kit/logger" @@ -108,8 +107,7 @@ func TestOAuth2ClientCredentialsToken(t *testing.T) { // Initialize middleware component and inject mocked TokenProvider log := logger.NewLogger("oauth2clientcredentials.test") - oauth2clientcredentialsMiddleware, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) - require.True(t, ok) + oauth2clientcredentialsMiddleware, _ := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) oauth2clientcredentialsMiddleware.SetTokenProvider(mockTokenProvider) handler, err := oauth2clientcredentialsMiddleware.GetHandler(t.Context(), metadata) require.NoError(t, err) @@ -169,8 +167,7 @@ func TestOAuth2ClientCredentialsCache(t *testing.T) { // Initialize middleware component and inject mocked TokenProvider log := logger.NewLogger("oauth2clientcredentials.test") - oauth2clientcredentialsMiddleware, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) - require.True(t, ok) + oauth2clientcredentialsMiddleware, _ := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) oauth2clientcredentialsMiddleware.SetTokenProvider(mockTokenProvider) handler, err := oauth2clientcredentialsMiddleware.GetHandler(t.Context(), metadata) require.NoError(t, err) @@ -202,103 +199,3 @@ func TestOAuth2ClientCredentialsCache(t *testing.T) { // Assertion assert.Equal(t, "MAC def", r.Header.Get("someHeader")) } - -func TestOAuth2ClientCredentialsPathFilterGetNativeMetadata(t *testing.T) { - log := logger.NewLogger("oauth2clientcredentials.test") - m, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) - require.True(t, ok) - - baseMiddlewareMetadata := middleware.Metadata{ - Base: metadata.Base{ - Properties: map[string]string{ - "clientID": "testId", - "clientSecret": "testSecret", - "scopes": "ascope", - "tokenURL": "https://localhost:9999", - "headerName": "someHeader", - "authStyle": "1", - }, - }, - } - - tc := []struct { - name string - pathFilter string - wantErr bool - }{ - {name: "empty pathFilter", pathFilter: "", wantErr: false}, - {name: "wildcard pathFilter", pathFilter: ".*", wantErr: false}, - {name: "api path pathFilter", pathFilter: "/api/v1/users", wantErr: false}, - {name: "debug endpoint pathFilter", pathFilter: "^/debug/?$", wantErr: false}, - {name: "user id pathFilter", pathFilter: "^/user/[0-9]+$", wantErr: false}, - {name: "invalid wildcard pathFilter", pathFilter: "*invalid", wantErr: true}, - {name: "unclosed parenthesis pathFilter", pathFilter: "invalid(", wantErr: true}, - {name: "unopened parenthesis pathFilter", pathFilter: "invalid)", wantErr: true}, - } - - for _, tt := range tc { - t.Run(tt.name, func(t *testing.T) { - baseMiddlewareMetadata.Properties["pathFilter"] = tt.pathFilter - _, err := m.getNativeMetadata(baseMiddlewareMetadata) - if tt.wantErr { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) - } -} - -func TestOAuth2ClientCredentialsPathFilterGetHandler(t *testing.T) { - // Setup - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - // Mock mockTokenProvider - mockTokenProvider := mock.NewMockTokenProviderInterface(mockCtrl) - - gomock.InOrder( - // First call returning abc and Bearer, expires within 1 second - mockTokenProvider. - EXPECT(). - GetToken(gomock.Any()). - Return(&oauth2.Token{ - AccessToken: "abcd", - TokenType: "Bearer", - Expiry: time.Now().In(time.UTC).Add(1 * time.Second), - }, nil). - Times(1), - ) - - var metadata middleware.Metadata - metadata.Properties = map[string]string{ - "clientID": "testId", - "clientSecret": "testSecret", - "scopes": "ascope", - "tokenURL": "https://localhost:9999", - "headerName": "authorization", - "authStyle": "1", - "pathFilter": "/api/v1/users/.*", - } - - log := logger.NewLogger("oauth2clientcredentials.test") - oauth2clientcredentialsMiddleware, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) - require.True(t, ok) - oauth2clientcredentialsMiddleware.SetTokenProvider(mockTokenProvider) - handler, err := oauth2clientcredentialsMiddleware.GetHandler(t.Context(), metadata) - require.NoError(t, err) - - // pathFilter should match - r := httptest.NewRequest(http.MethodGet, "http://dapr.io/api/v1/users/123", nil) - w := httptest.NewRecorder() - handler(http.HandlerFunc(mockedRequestHandler)).ServeHTTP(w, r) - - assert.Equal(t, "Bearer abcd", r.Header.Get("authorization")) - - // pathFilter should not match - r = httptest.NewRequest(http.MethodGet, "http://dapr.io/api/v1/tokens/123", nil) - w = httptest.NewRecorder() - handler(http.HandlerFunc(mockedRequestHandler)).ServeHTTP(w, r) - - assert.Equal(t, "", r.Header.Get("authorization")) -} diff --git a/middleware/http/opa/metadata.yaml b/middleware/http/opa/metadata.yaml deleted file mode 100644 index 84c93b126b..0000000000 --- a/middleware/http/opa/metadata.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: middleware -name: opa -version: v1 -status: alpha -title: "Open Policy Agent (OPA)" -description: | - The OPA middleware allows you to enforce policies on HTTP requests using Open Policy Agent (OPA) Rego policies. - It evaluates incoming requests against your Rego policies and can allow, deny, or modify requests based on the policy results. -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-opa/ -metadata: - - name: rego - type: string - required: true - description: "The Rego policy code that will be evaluated for each request. The policy package must be http and the policy must set data.http.allow" - - name: defaultStatus - type: number - required: false - description: "The status code to return for denied responses" - example: 403 - default: 403 - - name: includedHeaders - type: string - required: false - description: "Comma-separated set of case-insensitive headers to include in the request input. Request headers are not passed to the policy by default. Include to receive incoming request headers in the input" - example: "x-my-custom-header, x-jwt-header" - - name: readBody - type: string - required: false - description: "Controls whether the middleware reads the entire request body in-memory and make it available for policy decisions" - example: "false" diff --git a/middleware/http/opa/middleware.go b/middleware/http/opa/middleware.go index f2eecbbff7..5de765da68 100644 --- a/middleware/http/opa/middleware.go +++ b/middleware/http/opa/middleware.go @@ -42,7 +42,6 @@ import ( type Status int type middlewareMetadata struct { - // TODO: rego field needs to be updated to support the objects as input that the docs show for this field as inputs Rego string `json:"rego" mapstructure:"rego"` DefaultStatus Status `json:"defaultStatus,omitempty" mapstructure:"defaultStatus"` IncludedHeaders string `json:"includedHeaders,omitempty" mapstructure:"includedHeaders"` diff --git a/middleware/http/ratelimit/metadata.yaml b/middleware/http/ratelimit/metadata.yaml deleted file mode 100644 index fd29d14855..0000000000 --- a/middleware/http/ratelimit/metadata.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: middleware -name: ratelimit -version: v1 -status: stable -title: "Rate Limiting" -description: | - The Rate Limiting middleware provides request rate limiting functionality. - It can limit requests based on various criteria like IP address, user, or custom keys. -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-rate-limit/ -metadata: - - name: maxRequestsPerSecond - type: number - required: true - description: "Maximum number of requests allowed per second" - example: 100 \ No newline at end of file diff --git a/middleware/http/routeralias/routeralias_alias.go b/middleware/http/routeralias/routeralias_middleware.go similarity index 100% rename from middleware/http/routeralias/routeralias_alias.go rename to middleware/http/routeralias/routeralias_middleware.go diff --git a/middleware/http/routeralias/routeralias_alias_test.go b/middleware/http/routeralias/routeralias_middleware_test.go similarity index 100% rename from middleware/http/routeralias/routeralias_alias_test.go rename to middleware/http/routeralias/routeralias_middleware_test.go diff --git a/middleware/http/routerchecker/metadata.yaml b/middleware/http/routerchecker/metadata.yaml deleted file mode 100644 index d7ba54ff43..0000000000 --- a/middleware/http/routerchecker/metadata.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: middleware -name: routerchecker -version: v1 -status: alpha -title: "Router Checker" -description: | - The RouterChecker HTTP middleware component leverages regexp to check the validity of HTTP request routing to prevent invalid routers from entering the Dapr cluster. In turn, the RouterChecker component filters out bad requests and reduces noise in the telemetry and log data. -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-routerchecker/ -metadata: - - name: rule - type: string - required: true - description: "Regular expression to filter which paths are allowed" - example: "^[A-Za-z0-9/._-]+$" diff --git a/middleware/http/sentinel/metadata.yaml b/middleware/http/sentinel/metadata.yaml deleted file mode 100644 index c106454f45..0000000000 --- a/middleware/http/sentinel/metadata.yaml +++ /dev/null @@ -1,90 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: middleware -name: sentinel -version: v1 -status: alpha -title: "Sentinel" -description: | - Use Sentinel middleware to guarantee the reliability and resiliency of your application. - Sentinel is a powerful fault-tolerance component that takes "flow" as the breakthrough point and covers multiple fields including flow control, traffic shaping, concurrency limiting, circuit breaking, and adaptive system protection to guarantee the reliability and resiliency of microservices. - - The Sentinel HTTP middleware enables Dapr to facilitate Sentinel's powerful abilities to protect your application. You can refer to Sentinel Wiki for more details on Sentinel. -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-sentinel/ -metadata: - - name: appName - type: string - required: true - description: "The application name for Sentinel" - example: "nodeapp" - - name: logDir - type: string - required: false - description: "Directory for Sentinel log files" - example: "/var/tmp" - - name: flowRules - type: string - required: false - description: "JSON array of flow control rules to limit request rate" - example: | - [ - { - "resource": "POST:/v1.0/invoke/nodeapp/method/neworder", - "threshold": 10, - "tokenCalculateStrategy": 0, - "controlBehavior": 0 - } - ] - - name: circuitBreakerRules - type: string - required: false - description: "JSON array of circuit breaker rules to handle failures" - example: | - [ - { - "resource": "POST:/v1.0/invoke/nodeapp/method/neworder", - "minRequestAmount": 5, - "statIntervalMs": 1000, - "maxAllowedRtMs": 50, - "maxSlowRequestRatio": 0.5 - } - ] - - name: hotSpotParamRules - type: string - required: false - description: "JSON array of hotspot parameter rules for parameter-based flow control" - example: | - [ - { - "resource": "POST:/v1.0/invoke/nodeapp/method/neworder", - "paramIdx": 0, - "threshold": 10, - "maxQueueingTimeMs": 500 - } - ] - - name: isolationRules - type: string - required: false - description: "JSON array of isolation rules for thread pool isolation" - example: | - [ - { - "resource": "POST:/v1.0/invoke/nodeapp/method/neworder", - "maxConcurrency": 10, - "maxQueueingTimeMs": 500 - } - ] - - name: systemRules - type: string - required: false - description: "JSON array of system protection rules for overall system protection" - example: | - [ - { - "avgRt": 50, - "maxThread": 10, - "qps": 20 - } - ] diff --git a/middleware/http/wasm/metadata.yaml b/middleware/http/wasm/metadata.yaml deleted file mode 100644 index 26ae9fb586..0000000000 --- a/middleware/http/wasm/metadata.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: middleware -name: wasm -version: v1 -status: alpha -title: "WebAssembly (WASM)" -description: | - The WebAssembly middleware allows you to run custom logic written in WASM. - It can execute WASM modules to process HTTP requests and responses. -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-wasm/ -metadata: - - name: url - type: string - required: true - description: "URL of the WASM module" - example: "https://example.com/middleware.wasm" - - name: guestConfig - required: false - description: "Configuration object passed to the WASM module" - example: | - { - "timeout": "5s", - "maxMemory": "10MB" - } From 5b4b3b0911c82ad6522ee34c80264f132af601c8 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Mon, 11 Aug 2025 10:14:39 -0500 Subject: [PATCH 27/32] revert: checkout ps dir from main since in diff pr now Signed-off-by: Samantha Coyle --- pubsub/azure/servicebus/queues/servicebus.go | 2 +- pubsub/azure/servicebus/topics/servicebus.go | 2 +- pubsub/gcp/pubsub/pubsub.go | 2 +- pubsub/jetstream/metadata.yaml | 196 ------------------ pubsub/kubemq/metadata.yaml | 47 ----- pubsub/pubsub.go | 2 - pubsub/pulsar/metadata.go | 1 - pubsub/pulsar/metadata.yaml | 13 +- pubsub/pulsar/pulsar.go | 1 - pubsub/pulsar/pulsar_test.go | 42 ---- pubsub/redis/metadata.yaml | 26 +-- pubsub/rocketmq/metadata.go | 3 - pubsub/rocketmq/metadata.yaml | 202 ------------------- pubsub/rocketmq/rocketmq.go | 2 - 14 files changed, 5 insertions(+), 536 deletions(-) delete mode 100644 pubsub/jetstream/metadata.yaml delete mode 100644 pubsub/kubemq/metadata.yaml delete mode 100644 pubsub/rocketmq/metadata.yaml diff --git a/pubsub/azure/servicebus/queues/servicebus.go b/pubsub/azure/servicebus/queues/servicebus.go index 342a4e227e..4af688e3c5 100644 --- a/pubsub/azure/servicebus/queues/servicebus.go +++ b/pubsub/azure/servicebus/queues/servicebus.go @@ -57,7 +57,7 @@ func (a *azureServiceBus) Init(_ context.Context, metadata pubsub.Metadata) (err return err } - a.client, err = impl.NewClient(a.metadata, metadata.Properties, a.logger) + a.client, err = impl.NewClient(a.metadata, metadata.Properties) if err != nil { return err } diff --git a/pubsub/azure/servicebus/topics/servicebus.go b/pubsub/azure/servicebus/topics/servicebus.go index 1f83add614..2e6ceed6e3 100644 --- a/pubsub/azure/servicebus/topics/servicebus.go +++ b/pubsub/azure/servicebus/topics/servicebus.go @@ -58,7 +58,7 @@ func (a *azureServiceBus) Init(_ context.Context, metadata pubsub.Metadata) (err return err } - a.client, err = impl.NewClient(a.metadata, metadata.Properties, a.logger) + a.client, err = impl.NewClient(a.metadata, metadata.Properties) if err != nil { return err } diff --git a/pubsub/gcp/pubsub/pubsub.go b/pubsub/gcp/pubsub/pubsub.go index 7cf99cb694..d7e63627ec 100644 --- a/pubsub/gcp/pubsub/pubsub.go +++ b/pubsub/gcp/pubsub/pubsub.go @@ -170,7 +170,7 @@ func (g *GCPPubSub) Init(ctx context.Context, meta pubsub.Metadata) error { return nil } -func (g *GCPPubSub) getPubSubClient(_ context.Context, metadata *metadata) (*gcppubsub.Client, error) { +func (g *GCPPubSub) getPubSubClient(ctx context.Context, metadata *metadata) (*gcppubsub.Client, error) { var pubsubClient *gcppubsub.Client var err error diff --git a/pubsub/jetstream/metadata.yaml b/pubsub/jetstream/metadata.yaml deleted file mode 100644 index 9074ebb732..0000000000 --- a/pubsub/jetstream/metadata.yaml +++ /dev/null @@ -1,196 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: pubsub -name: jetstream -version: v1 -status: beta -title: "JetStream" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-pubsub/setup-jetstream/ -authenticationProfiles: - - title: "Basic Authentication" - description: "Connect to NATS JetStream without authentication." - metadata: - - name: natsURL - type: string - required: true - description: The NATS server URL. - example: "nats://localhost:4222" - - title: "Token Authentication" - description: "Connect to NATS JetStream using token authentication." - metadata: - - name: natsURL - type: string - required: true - description: The NATS server URL. - example: "nats://localhost:4222" - - name: token - type: string - required: true - description: The NATS authentication token. - example: "your-auth-token" - - title: "JWT Authentication" - description: "Connect to NATS JetStream using JWT authentication." - metadata: - - name: natsURL - type: string - required: true - description: The NATS server URL. - example: "nats://localhost:4222" - - name: jwt - type: string - required: true - description: The JWT token for authentication. - example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - - name: seedKey - type: string - required: true - description: The seed key for JWT authentication. - example: "SUAELX7XZIRRCNOQKCXQWFN6M3EBKUTBWK6YQKL4QFLKCUQJSEZL7ZCL7KQ" - - title: "TLS Authentication" - description: "Connect to NATS JetStream using TLS client certificates." - metadata: - - name: natsURL - type: string - required: true - description: The NATS server URL. - example: "nats://localhost:4222" - - name: tls_client_cert - type: string - required: true - description: The TLS client certificate. - example: | - -----BEGIN CERTIFICATE----- - XXX - -----END CERTIFICATE----- - - name: tls_client_key - type: string - required: true - description: The TLS client private key. - example: | - -----BEGIN PRIVATE KEY----- - XXX - -----END PRIVATE KEY----- -metadata: - - name: name - type: string - required: false - description: The name of the JetStream connection. - example: "dapr-jetstream" - default: "dapr.io - pubsub.jetstream" - - name: streamName - type: string - required: false - description: The name of the JetStream stream. - example: "my-stream" - - name: durableName - type: string - required: false - description: The durable name for the consumer. - example: "my-durable" - - name: queueGroupName - type: string - required: false - description: The queue group name for load balancing. - example: "my-queue-group" - - name: startSequence - type: number - required: false - description: The starting sequence number for message delivery. - example: 1 - - name: startTime - type: number - required: false - description: The starting time (Unix timestamp) for message delivery. - example: 1640995200 - default: 0 - - name: flowControl - type: bool - required: false - description: Enable flow control for the consumer. - example: false - default: false - - name: ackWait - type: string - required: false - description: The acknowledgment wait time. - example: "30s" - - name: maxDeliver - type: number - required: false - description: The maximum number of message deliveries. - example: 5 - - name: maxAckPending - type: number - required: false - description: The maximum number of unacknowledged messages. - example: 100 - - name: replicas - type: number - required: false - description: The number of stream replicas. - example: 3 - - name: memoryStorage - type: bool - required: false - description: Use memory storage for the stream. - example: false - default: false - - name: rateLimit - type: number - required: false - description: The rate limit for message consumption. - example: 1000 - - name: heartbeat - type: string - required: false - description: The heartbeat interval. - example: "30s" - - name: deliverPolicy - type: string - required: true - description: The delivery policy (all, last, new, sequence, time). - example: "all" - allowedValues: - - "all" - - "last" - - "new" - - "sequence" - - "time" - - name: ackPolicy - type: string - required: false - description: The acknowledgment policy. - example: "explicit" - allowedValues: - - "explicit" - - "all" - - "none" - default: "explicit" - - name: domain - type: string - required: false - description: The JetStream domain. - example: "my-domain" - - name: apiPrefix - type: string - required: false - description: The API prefix for JetStream. - example: "$JS.API" - - name: concurrency - type: string - required: false - description: The concurrency mode (single, parallel). - example: "single" - default: "single" - - name: backOff - type: string - required: false - description: The backoff configuration for message delivery for the consumer. - example: "1s" - - name: maxAckPending - type: number - required: false - description: The maximum number of unacknowledged messages for the consumer. - example: 100 \ No newline at end of file diff --git a/pubsub/kubemq/metadata.yaml b/pubsub/kubemq/metadata.yaml deleted file mode 100644 index d2d2a6dad8..0000000000 --- a/pubsub/kubemq/metadata.yaml +++ /dev/null @@ -1,47 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: pubsub -name: kubemq -version: v1 -status: beta -title: "KubeMQ" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-pubsub/setup-kubemq/ -authenticationProfiles: - - title: "Token Authentication" - description: "Connect to KubeMQ using an authentication token." - metadata: - - name: authToken - type: string - required: true - description: The authentication token for KubeMQ. - example: "your-auth-token" -metadata: - - name: address - type: string - required: true - description: The KubeMQ server address in format host:port. - example: "localhost:50000" - - name: clientID - type: string - required: false - description: The client ID for the KubeMQ connection. - example: "dapr-client" - - name: group - type: string - required: false - description: The consumer group name. - example: "my-group" - - name: store - type: bool - required: false - description: Whether to use KubeMQ Event Store (true) or Events (false). - example: true - default: true - - name: disableReDelivery - type: bool - required: false - description: Disable message re-delivery on error. - example: false - default: false \ No newline at end of file diff --git a/pubsub/pubsub.go b/pubsub/pubsub.go index 47377d65e2..f61261108e 100644 --- a/pubsub/pubsub.go +++ b/pubsub/pubsub.go @@ -22,8 +22,6 @@ import ( "github.com/dapr/components-contrib/metadata" ) -var ErrGracefulShutdown = errors.New("pubsub shutdown") - // PubSub is the interface for message buses. type PubSub interface { metadata.ComponentWithMetadata diff --git a/pubsub/pulsar/metadata.go b/pubsub/pulsar/metadata.go index c373005425..b3f54c1336 100644 --- a/pubsub/pulsar/metadata.go +++ b/pubsub/pulsar/metadata.go @@ -39,7 +39,6 @@ type pulsarMetadata struct { ReceiverQueueSize int `mapstructure:"receiverQueueSize"` SubscriptionType string `mapstructure:"subscribeType"` SubscriptionInitialPosition string `mapstructure:"subscribeInitialPosition"` - ReplicateSubscriptionState bool `mapstructure:"replicateSubscriptionState"` SubscriptionMode string `mapstructure:"subscribeMode"` Token string `mapstructure:"token"` oauth2.ClientCredentialsMetadata `mapstructure:",squash"` diff --git a/pubsub/pulsar/metadata.yaml b/pubsub/pulsar/metadata.yaml index 72c5fd5a30..8277583055 100644 --- a/pubsub/pulsar/metadata.yaml +++ b/pubsub/pulsar/metadata.yaml @@ -202,17 +202,6 @@ metadata: url: title: "Pulsar SubscriptionInitialPosition" url: "https://pkg.go.dev/github.com/apache/pulsar-client-go/pulsar#SubscriptionInitialPosition" - - name: replicateSubscriptionState - type: bool - description: | - Enable replication of subscription state across geo-replicated Pulsar clusters. - When enabled, subscription state (such as cursor positions and acknowledgments) will be replicated to other clusters in a geo-replicated setup. - This is useful for maintaining subscription consistency during cluster failovers. - default: 'false' - example: '"true", "false"' - url: - title: "Pulsar Geo-Replication" - url: "https://pulsar.apache.org/docs/administration-geo/" - name: subscribeMode type: string description: | @@ -221,4 +210,4 @@ metadata: example: '"durable"' url: title: "Pulsar SubscriptionMode" - url: "https://pkg.go.dev/github.com/apache/pulsar-client-go/pulsar#SubscriptionMode" + url: "https://pkg.go.dev/github.com/apache/pulsar-client-go/pulsar#SubscriptionMode" \ No newline at end of file diff --git a/pubsub/pulsar/pulsar.go b/pubsub/pulsar/pulsar.go index 1ce44dc209..56c1d9b322 100644 --- a/pubsub/pulsar/pulsar.go +++ b/pubsub/pulsar/pulsar.go @@ -509,7 +509,6 @@ func (p *Pulsar) Subscribe(ctx context.Context, req pubsub.SubscribeRequest, han MessageChannel: channel, NackRedeliveryDelay: p.metadata.RedeliveryDelay, ReceiverQueueSize: p.metadata.ReceiverQueueSize, - ReplicateSubscriptionState: p.metadata.ReplicateSubscriptionState, } // Handle KeySharedPolicy for key_shared subscription type diff --git a/pubsub/pulsar/pulsar_test.go b/pubsub/pulsar/pulsar_test.go index eff822515b..7ade8c9fc9 100644 --- a/pubsub/pulsar/pulsar_test.go +++ b/pubsub/pulsar/pulsar_test.go @@ -605,48 +605,6 @@ func TestEncryptionKeys(t *testing.T) { }) } -func TestParsePulsarMetadataReplicateSubscriptionState(t *testing.T) { - tt := []struct { - name string - replicateSubscriptionState string - expected bool - }{ - { - name: "test replicateSubscriptionState true", - replicateSubscriptionState: "true", - expected: true, - }, - { - name: "test replicateSubscriptionState false", - replicateSubscriptionState: "false", - expected: false, - }, - { - name: "test replicateSubscriptionState empty (defaults to false)", - replicateSubscriptionState: "", - expected: false, - }, - } - - for _, tc := range tt { - t.Run(tc.name, func(t *testing.T) { - m := pubsub.Metadata{} - m.Properties = map[string]string{ - "host": "a", - } - - if tc.replicateSubscriptionState != "" { - m.Properties["replicateSubscriptionState"] = tc.replicateSubscriptionState - } - - meta, err := parsePulsarMetadata(m) - - require.NoError(t, err) - assert.Equal(t, tc.expected, meta.ReplicateSubscriptionState) - }) - } -} - func TestSanitiseURL(t *testing.T) { tests := []struct { name string diff --git a/pubsub/redis/metadata.yaml b/pubsub/redis/metadata.yaml index 5e0684d1e1..ed852f2076 100644 --- a/pubsub/redis/metadata.yaml +++ b/pubsub/redis/metadata.yaml @@ -31,30 +31,6 @@ authenticationProfiles: secret reference example: "KeFg23!" default: "" - - name: sentinelUsername - type: string - required: false - description: | - Username for Redis Sentinel. Applicable only when "failover" is true, and - Redis Sentinel has authentication enabled. Defaults to empty. - example: "my-sentinel-username" - default: "" - url: - title: "Redis Sentinel authentication documentation" - url: "https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/#configuring-sentinel-instances-with-authentication" - - name: sentinelPassword - type: string - required: false - sensitive: true - description: | - Password for Redis Sentinel. Applicable only when "failover" is true, and - Redis Sentinel has authentication enabled. Use secretKeyRef for - secret reference. Defaults to empty. - example: "KeFg23!" - default: "" - url: - title: "Redis Sentinel authentication documentation" - url: "https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/#configuring-sentinel-instances-with-authentication" metadata: - name: redisHost required: true @@ -210,7 +186,7 @@ metadata: - name: sentinelMasterName required: false description: The sentinel master name. See Redis Sentinel Documentation. - example: "mymaster" + example: "127.0.0.1:6379" type: string - name: maxLenApprox required: false diff --git a/pubsub/rocketmq/metadata.go b/pubsub/rocketmq/metadata.go index 4817e01b9f..a26d561bba 100644 --- a/pubsub/rocketmq/metadata.go +++ b/pubsub/rocketmq/metadata.go @@ -42,9 +42,6 @@ const ( DaprQueueSelector QueueSelectorType = "dapr" ) -// TODO: the time fields in the metadata need to be standardized on either seconds or milliseconds or minutes. -// Right now, it's a mix of all three. - // RocketMQ Go Client Options type rocketMQMetaData struct { // rocketmq instance name, it will be registered to the broker diff --git a/pubsub/rocketmq/metadata.yaml b/pubsub/rocketmq/metadata.yaml deleted file mode 100644 index 93b647a066..0000000000 --- a/pubsub/rocketmq/metadata.yaml +++ /dev/null @@ -1,202 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: pubsub -name: rocketmq -version: v1 -status: alpha -title: "RocketMQ" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-pubsub/setup-rocketmq/ -authenticationProfiles: - - title: "Credential Authentication" - description: "Connect to RocketMQ using access key and secret key." - metadata: - - name: accessKey - type: string - required: true - description: The RocketMQ access key. - example: "your-access-key" - - name: secretKey - type: string - required: true - description: The RocketMQ secret key. - example: "your-secret-key" - - name: securityToken - type: string - required: false - description: The RocketMQ security token. - example: "your-security-token" -metadata: - - name: nameServer - type: string - required: false - description: The RocketMQ name server address. - example: "localhost:9876" - - name: instanceName - type: string - required: false - description: The RocketMQ instance name. - example: "dapr-rocketmq" - - name: producerGroup - type: string - required: false - description: The producer group name. - example: "dapr-producer-group" - - name: consumerGroup - type: string - required: false - description: The consumer group name. - example: "dapr-consumer-group" - - name: nameSpace - type: string - required: false - description: The RocketMQ namespace. - example: "my-namespace" - - name: nameServerDomain - type: string - required: false - description: The RocketMQ name server domain. - example: "rocketmq.example.com" - - name: retries - type: number - required: false - description: The number of retry attempts for sending messages. - example: 3 - default: 3 - - name: producerQueueSelector - type: string - required: false - description: The producer queue selector type. - example: "hash" - allowedValues: - - "hash" - - "random" - - "manual" - - "roundRobin" - - "dapr" - - name: consumerModel - type: string - required: false - description: The consumer model (clustering, broadcasting). - example: "clustering" - default: "clustering" - - name: fromWhere - type: string - required: false - description: The consumption starting point. - example: "ConsumeFromLastOffset" - allowedValues: - - "ConsumeFromLastOffset" - - "ConsumeFromFirstOffset" - - "ConsumeFromTimestamp" - - name: consumeTimestamp - type: string - required: false - description: The timestamp for consumption starting point. - example: "20220817101902" - - name: consumeOrderly - type: bool - required: false - description: Enable orderly message consumption. - example: false - default: false - - name: consumeMessageBatchMaxSize - type: number - required: false - description: The maximum batch size for message consumption. - example: 10 - - name: consumeConcurrentlyMaxSpan - type: number - required: false - description: The maximum span for concurrent consumption. - example: 10 - - name: maxReconsumeTimes - type: number - required: false - description: The maximum number of reconsume times. -1 means 16 times. - example: 10000 - - name: autoCommit - type: bool - required: false - description: Enable automatic commit. - example: true - default: false - - name: consumeTimeout - type: number - required: false - description: The consume timeout in minutes. - example: 10 - - name: consumerPullTimeout - type: number - required: false - description: The consumer pull timeout in milliseconds. - example: 30 - default: 30 - - name: pullInterval - type: number - required: false - description: The pull interval in minutes. - example: 100 - default: 100 - - name: consumerBatchSize - type: number - required: false - description: The consumer batch size. - example: 10 - - name: pullBatchSize - type: number - required: false - description: The pull batch size. - example: 10 - - name: pullThresholdForQueue - type: number - required: false - description: The pull threshold for queue. - example: 100 - - name: pullThresholdForTopic - type: number - required: false - description: The pull threshold for topic. - example: 100 - - name: pullThresholdSizeForQueue - type: number - required: false - description: The pull threshold size for queue. - example: 10 - - name: pullThresholdSizeForTopic - type: number - required: false - description: The pull threshold size for topic. - example: 10 - - name: content-type - type: string - required: false - description: The content type for messages. - example: "json" - - name: sendTimeOutSec - type: number - required: false - description: The send timeout in seconds. - example: 10 - - name: logLevel - type: string - required: false - description: The log level. - example: "warn" - default: "warn" - - name: mspProperties - type: string - required: false - description: The message properties to pass to the application (comma-separated). - example: "UNIQ_KEY,MSG_ID" - - name: groupName - type: string - required: false - description: The consumer group name (deprecated, use consumerGroup instead). - example: "dapr-consumer-group" - - name: sendTimeOut - type: number - required: false - description: The send timeout in nanoseconds (deprecated, use sendTimeOutSec instead). - example: 10000000000 \ No newline at end of file diff --git a/pubsub/rocketmq/rocketmq.go b/pubsub/rocketmq/rocketmq.go index d1051a9ba6..c7fcdece99 100644 --- a/pubsub/rocketmq/rocketmq.go +++ b/pubsub/rocketmq/rocketmq.go @@ -121,7 +121,6 @@ func parseNameServer(nameServer string) []string { } } -// TODO: these metadata fields need to be reevaluated on required vs not. func (r *rocketMQ) setUpConsumer() (mq.PushConsumer, error) { opts := make([]mqc.Option, 0) if r.metadata.InstanceName != "" { @@ -244,7 +243,6 @@ func (r *rocketMQ) setUpConsumer() (mq.PushConsumer, error) { return c, e } -// TODO: these metadata fields need to be reevaluated on required vs not. func (r *rocketMQ) setUpProducer() (mq.Producer, error) { opts := make([]mqp.Option, 0) if r.metadata.InstanceName != "" { From e3f9f167754fc36deff34a84835b5e84819df2ae Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Mon, 11 Aug 2025 10:15:48 -0500 Subject: [PATCH 28/32] fix: grab latest from master and reset these files to updated master Signed-off-by: Samantha Coyle --- .../servicebusqueues/servicebusqueues.go | 2 +- bindings/gcp/bucket/bucket.go | 43 +++++-- bindings/gcp/bucket/bucket_test.go | 25 ++++ bindings/redis/metadata.yaml | 26 +++- middleware/http/oauth2/oauth2_middleware.go | 22 ++++ .../http/oauth2/oauth2_middleware_test.go | 49 ++++++++ .../oauth2clientcredentials_middleware.go | 53 +++++--- ...ntcredentials_middleware_benchmark_test.go | 113 ++++++++++++++++++ ...oauth2clientcredentials_middleware_test.go | 107 ++++++++++++++++- pubsub/azure/servicebus/queues/servicebus.go | 2 +- pubsub/azure/servicebus/topics/servicebus.go | 2 +- pubsub/pubsub.go | 2 + pubsub/pulsar/metadata.go | 1 + pubsub/pulsar/metadata.yaml | 13 +- pubsub/pulsar/pulsar.go | 1 + pubsub/pulsar/pulsar_test.go | 42 +++++++ pubsub/redis/metadata.yaml | 26 +++- state/alicloud/tablestore/mock_client.go | 12 ++ state/oracledatabase/oracledatabaseaccess.go | 16 ++- state/redis/metadata.yaml | 26 +++- state/sqlserver/sqlserver.go | 34 ++++-- state/sqlserver/sqlserver_integration_test.go | 30 ++++- 22 files changed, 601 insertions(+), 46 deletions(-) create mode 100644 middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_benchmark_test.go diff --git a/bindings/azure/servicebusqueues/servicebusqueues.go b/bindings/azure/servicebusqueues/servicebusqueues.go index af2bc3b534..bc1291ac32 100644 --- a/bindings/azure/servicebusqueues/servicebusqueues.go +++ b/bindings/azure/servicebusqueues/servicebusqueues.go @@ -62,7 +62,7 @@ func (a *AzureServiceBusQueues) Init(ctx context.Context, metadata bindings.Meta return err } - a.client, err = impl.NewClient(a.metadata, metadata.Properties) + a.client, err = impl.NewClient(a.metadata, metadata.Properties, a.logger) if err != nil { return err } diff --git a/bindings/gcp/bucket/bucket.go b/bindings/gcp/bucket/bucket.go index 155d96abea..027702f572 100644 --- a/bindings/gcp/bucket/bucket.go +++ b/bindings/gcp/bucket/bucket.go @@ -110,13 +110,7 @@ func (g *GCPStorage) Init(ctx context.Context, metadata bindings.Metadata) error return err } - b, err := json.Marshal(m) - if err != nil { - return err - } - - clientOptions := option.WithCredentialsJSON(b) - client, err := storage.NewClient(ctx, clientOptions) + client, err := g.getClient(ctx, m) if err != nil { return err } @@ -127,6 +121,41 @@ func (g *GCPStorage) Init(ctx context.Context, metadata bindings.Metadata) error return nil } +func (g *GCPStorage) getClient(ctx context.Context, m *gcpMetadata) (*storage.Client, error) { + var client *storage.Client + var err error + + if m.Bucket == "" { + return nil, errors.New("missing property `bucket` in metadata") + } + if m.ProjectID == "" { + return nil, errors.New("missing property `project_id` in metadata") + } + + // Explicit authentication + if m.PrivateKeyID != "" { + var b []byte + b, err = json.Marshal(m) + if err != nil { + return nil, err + } + + clientOptions := option.WithCredentialsJSON(b) + client, err = storage.NewClient(ctx, clientOptions) + if err != nil { + return nil, err + } + } else { + // Implicit authentication, using GCP Application Default Credentials (ADC) + // Credentials search order: https://cloud.google.com/docs/authentication/application-default-credentials#order + client, err = storage.NewClient(ctx) + if err != nil { + return nil, err + } + } + return client, nil +} + func (g *GCPStorage) parseMetadata(meta bindings.Metadata) (*gcpMetadata, error) { m := gcpMetadata{} err := kitmd.DecodeMetadata(meta.Properties, &m) diff --git a/bindings/gcp/bucket/bucket_test.go b/bindings/gcp/bucket/bucket_test.go index e5200c00c6..09392f044e 100644 --- a/bindings/gcp/bucket/bucket_test.go +++ b/bindings/gcp/bucket/bucket_test.go @@ -15,6 +15,7 @@ package bucket import ( "encoding/json" + "errors" "testing" "github.com/stretchr/testify/assert" @@ -234,6 +235,30 @@ func TestMergeWithRequestMetadata(t *testing.T) { }) } +func TestInit(t *testing.T) { + t.Run("Init missing bucket from metadata", func(t *testing.T) { + m := bindings.Metadata{} + m.Properties = map[string]string{ + "projectID": "my_project_id", + } + gs := GCPStorage{logger: logger.NewLogger("test")} + err := gs.Init(t.Context(), m) + require.Error(t, err) + assert.Equal(t, err, errors.New("missing property `bucket` in metadata")) + }) + + t.Run("Init missing projectID from metadata", func(t *testing.T) { + m := bindings.Metadata{} + m.Properties = map[string]string{ + "bucket": "my_bucket", + } + gs := GCPStorage{logger: logger.NewLogger("test")} + err := gs.Init(t.Context(), m) + require.Error(t, err) + assert.Equal(t, err, errors.New("missing property `project_id` in metadata")) + }) +} + func TestGetOption(t *testing.T) { gs := GCPStorage{logger: logger.NewLogger("test")} gs.metadata = &gcpMetadata{} diff --git a/bindings/redis/metadata.yaml b/bindings/redis/metadata.yaml index efc3407501..ad2bd7c62c 100644 --- a/bindings/redis/metadata.yaml +++ b/bindings/redis/metadata.yaml @@ -42,6 +42,30 @@ authenticationProfiles: secret reference example: "KeFg23!" default: "" + - name: sentinelUsername + type: string + required: false + description: | + Username for Redis Sentinel. Applicable only when "failover" is true, and + Redis Sentinel has authentication enabled. Defaults to empty. + example: "my-sentinel-username" + default: "" + url: + title: "Redis Sentinel authentication documentation" + url: "https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/#configuring-sentinel-instances-with-authentication" + - name: sentinelPassword + type: string + required: false + sensitive: true + description: | + Password for Redis Sentinel. Applicable only when "failover" is true, and + Redis Sentinel has authentication enabled. Use secretKeyRef for + secret reference. Defaults to empty. + example: "KeFg23!" + default: "" + url: + title: "Redis Sentinel authentication documentation" + url: "https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/#configuring-sentinel-instances-with-authentication" metadata: - name: redisHost required: true @@ -104,7 +128,7 @@ metadata: required: false description: | The Redis sentinel master name. Required when "failover" is enabled. - example: "127.0.0.1:6379" + example: "mymaster" url: title: "Redis Sentinel documentation" url: "https://redis.io/docs/manual/sentinel/" diff --git a/middleware/http/oauth2/oauth2_middleware.go b/middleware/http/oauth2/oauth2_middleware.go index 4c7b4b6629..2211caa58b 100644 --- a/middleware/http/oauth2/oauth2_middleware.go +++ b/middleware/http/oauth2/oauth2_middleware.go @@ -18,6 +18,7 @@ import ( "net/http" "net/url" "reflect" + "regexp" "strings" "github.com/fasthttp-contrib/sessions" @@ -42,6 +43,9 @@ type oAuth2MiddlewareMetadata struct { AuthHeaderName string `json:"authHeaderName" mapstructure:"authHeaderName"` RedirectURL string `json:"redirectURL" mapstructure:"redirectURL"` ForceHTTPS string `json:"forceHTTPS" mapstructure:"forceHTTPS"` + PathFilter string `json:"pathFilter" mapstructure:"pathFilter"` + + pathFilterRegex *regexp.Regexp } // NewOAuth2Middleware returns a new oAuth2 middleware. @@ -84,6 +88,15 @@ func (m *Middleware) GetHandler(ctx context.Context, metadata middleware.Metadat return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if meta.pathFilterRegex != nil { + matched := meta.pathFilterRegex.MatchString(r.URL.Path) + if !matched { + m.logger.Debugf("PathFilter %s didn't match %s! Skipping!", meta.PathFilter, r.URL.Path) + next.ServeHTTP(w, r) + return + } + } + session := sessions.Start(w, r) if session.GetString(meta.AuthHeaderName) != "" { @@ -153,6 +166,15 @@ func (m *Middleware) getNativeMetadata(metadata middleware.Metadata) (*oAuth2Mid if err != nil { return nil, err } + + if middlewareMetadata.PathFilter != "" { + rx, err := regexp.Compile(middlewareMetadata.PathFilter) + if err != nil { + return nil, err + } + middlewareMetadata.pathFilterRegex = rx + } + return &middlewareMetadata, nil } diff --git a/middleware/http/oauth2/oauth2_middleware_test.go b/middleware/http/oauth2/oauth2_middleware_test.go index 4f12a5825c..e8f67e4183 100644 --- a/middleware/http/oauth2/oauth2_middleware_test.go +++ b/middleware/http/oauth2/oauth2_middleware_test.go @@ -61,3 +61,52 @@ func TestOAuth2CreatesAuthorizationHeaderWhenInSessionState(t *testing.T) { assert.Equal(t, "Bearer abcd", r.Header.Get("someHeader")) } + +func TestOAuth2CreatesAuthorizationHeaderGetNativeMetadata(t *testing.T) { + var metadata middleware.Metadata + metadata.Properties = map[string]string{ + "clientID": "testId", + "clientSecret": "testSecret", + "scopes": "ascope", + "authURL": "https://idp:9999", + "tokenURL": "https://idp:9999", + "redirectUrl": "https://localhost:9999", + "authHeaderName": "someHeader", + } + + log := logger.NewLogger("oauth2.test") + oauth2Middleware, ok := NewOAuth2Middleware(log).(*Middleware) + require.True(t, ok) + + tc := []struct { + name string + pathFilter string + wantErr bool + }{ + {name: "empty pathFilter", pathFilter: "", wantErr: false}, + {name: "wildcard pathFilter", pathFilter: ".*", wantErr: false}, + {name: "api path pathFilter", pathFilter: "/api/v1/users", wantErr: false}, + {name: "debug endpoint pathFilter", pathFilter: "^/debug/?$", wantErr: false}, + {name: "user id pathFilter", pathFilter: "^/user/[0-9]+$", wantErr: false}, + {name: "invalid wildcard pathFilter", pathFilter: "*invalid", wantErr: true}, + {name: "unclosed parenthesis pathFilter", pathFilter: "invalid(", wantErr: true}, + {name: "unopened parenthesis pathFilter", pathFilter: "invalid)", wantErr: true}, + } + + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + metadata.Properties["pathFilter"] = tt.pathFilter + nativeMetadata, err := oauth2Middleware.getNativeMetadata(metadata) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + if tt.pathFilter != "" { + require.NotNil(t, nativeMetadata.pathFilterRegex) + } else { + require.Nil(t, nativeMetadata.pathFilterRegex) + } + } + }) + } +} diff --git a/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware.go b/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware.go index 484376efe3..12f955d641 100644 --- a/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware.go +++ b/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware.go @@ -21,6 +21,7 @@ import ( "net/http" "net/url" "reflect" + "regexp" "strings" "time" @@ -43,6 +44,9 @@ type oAuth2ClientCredentialsMiddlewareMetadata struct { HeaderName string `json:"headerName" mapstructure:"headerName"` EndpointParamsQuery string `json:"endpointParamsQuery,omitempty" mapstructure:"endpointParamsQuery"` AuthStyle int `json:"authStyle" mapstructure:"authStyle"` + PathFilter string `json:"pathFilter" mapstructure:"pathFilter"` + + pathFilterRegex *regexp.Regexp } // TokenProviderInterface provides a common interface to Mock the Token retrieval in unit tests. @@ -69,7 +73,7 @@ type Middleware struct { tokenProvider TokenProviderInterface } -// GetHandler retruns the HTTP handler provided by the middleware. +// GetHandler returns the HTTP handler provided by the middleware. func (m *Middleware) GetHandler(_ context.Context, metadata middleware.Metadata) (func(next http.Handler) http.Handler, error) { meta, err := m.getNativeMetadata(metadata) if err != nil { @@ -98,27 +102,38 @@ func (m *Middleware) GetHandler(_ context.Context, metadata middleware.Metadata) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var headerValue string - // Check if valid token is in the cache - cachedToken, found := m.tokenCache.Get(cacheKey) - if !found { - m.log.Debugf("Cached token not found, try get one") - - token, err := m.tokenProvider.GetToken(r.Context(), conf) - if err != nil { - m.log.Errorf("Error acquiring token: %s", err) + if meta.pathFilterRegex != nil { + matched := meta.pathFilterRegex.MatchString(r.URL.Path) + if !matched { + m.log.Debugf("PathFilter %s didn't match %s! Skipping!", meta.PathFilter, r.URL.Path) + next.ServeHTTP(w, r) return } + } - tokenExpirationDuration := time.Until(token.Expiry) - m.log.Debugf("Token expires at %s (%s from now)", token.Expiry, tokenExpirationDuration) - - headerValue = token.Type() + " " + token.AccessToken - m.tokenCache.Set(cacheKey, headerValue, tokenExpirationDuration) - } else { + // Check if valid token is in the cache + cachedToken, found := m.tokenCache.Get(cacheKey) + if found { m.log.Debugf("Cached token found for key %s", cacheKey) headerValue = cachedToken.(string) + r.Header.Add(meta.HeaderName, headerValue) + next.ServeHTTP(w, r) + return + } + + m.log.Infof("Cached token not found, attempting to retrieve a new one") + token, err := m.tokenProvider.GetToken(r.Context(), conf) + if err != nil { + m.log.Errorf("Error acquiring token: %s", err) + return } + tokenExpirationDuration := time.Until(token.Expiry) + m.log.Infof("Token expires at %s (%s from now)", token.Expiry, tokenExpirationDuration) + + headerValue = token.Type() + " " + token.AccessToken + m.tokenCache.Set(cacheKey, headerValue, tokenExpirationDuration) + r.Header.Add(meta.HeaderName, headerValue) next.ServeHTTP(w, r) }) @@ -142,6 +157,14 @@ func (m *Middleware) getNativeMetadata(metadata middleware.Metadata) (*oAuth2Cli m.checkMetadataValueExists(&errorString, &middlewareMetadata.Scopes, "scopes") m.checkMetadataValueExists(&errorString, &middlewareMetadata.TokenURL, "tokenURL") + if middlewareMetadata.PathFilter != "" { + rx, err := regexp.Compile(middlewareMetadata.PathFilter) + if err != nil { + errorString += "Parameter 'pathFilter' is not a valid regex: " + err.Error() + ". " + } + middlewareMetadata.pathFilterRegex = rx + } + // Value-check AuthStyle if middlewareMetadata.AuthStyle < 0 || middlewareMetadata.AuthStyle > 2 { errorString += fmt.Sprintf("Parameter 'authStyle' can only have the values 0,1,2. Received: '%d'. ", middlewareMetadata.AuthStyle) diff --git a/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_benchmark_test.go b/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_benchmark_test.go new file mode 100644 index 0000000000..032cbc11d6 --- /dev/null +++ b/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_benchmark_test.go @@ -0,0 +1,113 @@ +/* +Copyright 2025 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package oauth2clientcredentials + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + "golang.org/x/oauth2" + + "github.com/dapr/components-contrib/middleware" + mock "github.com/dapr/components-contrib/middleware/http/oauth2clientcredentials/mocks" + "github.com/dapr/kit/logger" +) + +func BenchmarkTestOAuth2ClientCredentialsGetHandler(b *testing.B) { + mockCtrl := gomock.NewController(b) + defer mockCtrl.Finish() + mockTokenProvider := mock.NewMockTokenProviderInterface(mockCtrl) + gomock.InOrder( + mockTokenProvider. + EXPECT(). + GetToken(gomock.Any()). + Return(&oauth2.Token{ + AccessToken: "abcd", + TokenType: "Bearer", + Expiry: time.Now().Add(1 * time.Minute), + }, nil). + Times(1), + ) + + var metadata middleware.Metadata + metadata.Properties = map[string]string{ + "clientID": "testId", + "clientSecret": "testSecret", + "scopes": "ascope", + "tokenURL": "https://localhost:9999", + "headerName": "authorization", + "authStyle": "1", + } + + log := logger.NewLogger("oauth2clientcredentials.test") + oauth2clientcredentialsMiddleware, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) + require.True(b, ok) + oauth2clientcredentialsMiddleware.SetTokenProvider(mockTokenProvider) + handler, err := oauth2clientcredentialsMiddleware.GetHandler(b.Context(), metadata) + require.NoError(b, err) + + for i := range b.N { + url := fmt.Sprintf("http://dapr.io/api/v1/users/%d", i) + r := httptest.NewRequest(http.MethodGet, url, nil) + w := httptest.NewRecorder() + handler(http.HandlerFunc(mockedRequestHandler)).ServeHTTP(w, r) + } +} + +func BenchmarkTestOAuth2ClientCredentialsGetHandlerWithPathFilter(b *testing.B) { + mockCtrl := gomock.NewController(b) + defer mockCtrl.Finish() + mockTokenProvider := mock.NewMockTokenProviderInterface(mockCtrl) + gomock.InOrder( + mockTokenProvider. + EXPECT(). + GetToken(gomock.Any()). + Return(&oauth2.Token{ + AccessToken: "abcd", + TokenType: "Bearer", + Expiry: time.Now().Add(1 * time.Minute), + }, nil). + Times(1), + ) + + var metadata middleware.Metadata + metadata.Properties = map[string]string{ + "clientID": "testId", + "clientSecret": "testSecret", + "scopes": "ascope", + "tokenURL": "https://localhost:9999", + "headerName": "authorization", + "authStyle": "1", + "pathFilter": "/api/v1/users/.*", + } + + log := logger.NewLogger("oauth2clientcredentials.test") + oauth2clientcredentialsMiddleware, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) + require.True(b, ok) + oauth2clientcredentialsMiddleware.SetTokenProvider(mockTokenProvider) + handler, err := oauth2clientcredentialsMiddleware.GetHandler(b.Context(), metadata) + require.NoError(b, err) + + for i := range b.N { + url := fmt.Sprintf("http://dapr.io/api/v1/users/%d", i) + r := httptest.NewRequest(http.MethodGet, url, nil) + w := httptest.NewRecorder() + handler(http.HandlerFunc(mockedRequestHandler)).ServeHTTP(w, r) + } +} diff --git a/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_test.go b/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_test.go index 48d27754e4..7fdbc86c69 100644 --- a/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_test.go +++ b/middleware/http/oauth2clientcredentials/oauth2clientcredentials_middleware_test.go @@ -24,6 +24,7 @@ import ( "github.com/stretchr/testify/require" oauth2 "golang.org/x/oauth2" + "github.com/dapr/components-contrib/metadata" "github.com/dapr/components-contrib/middleware" mock "github.com/dapr/components-contrib/middleware/http/oauth2clientcredentials/mocks" "github.com/dapr/kit/logger" @@ -107,7 +108,8 @@ func TestOAuth2ClientCredentialsToken(t *testing.T) { // Initialize middleware component and inject mocked TokenProvider log := logger.NewLogger("oauth2clientcredentials.test") - oauth2clientcredentialsMiddleware, _ := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) + oauth2clientcredentialsMiddleware, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) + require.True(t, ok) oauth2clientcredentialsMiddleware.SetTokenProvider(mockTokenProvider) handler, err := oauth2clientcredentialsMiddleware.GetHandler(t.Context(), metadata) require.NoError(t, err) @@ -167,7 +169,8 @@ func TestOAuth2ClientCredentialsCache(t *testing.T) { // Initialize middleware component and inject mocked TokenProvider log := logger.NewLogger("oauth2clientcredentials.test") - oauth2clientcredentialsMiddleware, _ := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) + oauth2clientcredentialsMiddleware, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) + require.True(t, ok) oauth2clientcredentialsMiddleware.SetTokenProvider(mockTokenProvider) handler, err := oauth2clientcredentialsMiddleware.GetHandler(t.Context(), metadata) require.NoError(t, err) @@ -199,3 +202,103 @@ func TestOAuth2ClientCredentialsCache(t *testing.T) { // Assertion assert.Equal(t, "MAC def", r.Header.Get("someHeader")) } + +func TestOAuth2ClientCredentialsPathFilterGetNativeMetadata(t *testing.T) { + log := logger.NewLogger("oauth2clientcredentials.test") + m, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) + require.True(t, ok) + + baseMiddlewareMetadata := middleware.Metadata{ + Base: metadata.Base{ + Properties: map[string]string{ + "clientID": "testId", + "clientSecret": "testSecret", + "scopes": "ascope", + "tokenURL": "https://localhost:9999", + "headerName": "someHeader", + "authStyle": "1", + }, + }, + } + + tc := []struct { + name string + pathFilter string + wantErr bool + }{ + {name: "empty pathFilter", pathFilter: "", wantErr: false}, + {name: "wildcard pathFilter", pathFilter: ".*", wantErr: false}, + {name: "api path pathFilter", pathFilter: "/api/v1/users", wantErr: false}, + {name: "debug endpoint pathFilter", pathFilter: "^/debug/?$", wantErr: false}, + {name: "user id pathFilter", pathFilter: "^/user/[0-9]+$", wantErr: false}, + {name: "invalid wildcard pathFilter", pathFilter: "*invalid", wantErr: true}, + {name: "unclosed parenthesis pathFilter", pathFilter: "invalid(", wantErr: true}, + {name: "unopened parenthesis pathFilter", pathFilter: "invalid)", wantErr: true}, + } + + for _, tt := range tc { + t.Run(tt.name, func(t *testing.T) { + baseMiddlewareMetadata.Properties["pathFilter"] = tt.pathFilter + _, err := m.getNativeMetadata(baseMiddlewareMetadata) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestOAuth2ClientCredentialsPathFilterGetHandler(t *testing.T) { + // Setup + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + // Mock mockTokenProvider + mockTokenProvider := mock.NewMockTokenProviderInterface(mockCtrl) + + gomock.InOrder( + // First call returning abc and Bearer, expires within 1 second + mockTokenProvider. + EXPECT(). + GetToken(gomock.Any()). + Return(&oauth2.Token{ + AccessToken: "abcd", + TokenType: "Bearer", + Expiry: time.Now().In(time.UTC).Add(1 * time.Second), + }, nil). + Times(1), + ) + + var metadata middleware.Metadata + metadata.Properties = map[string]string{ + "clientID": "testId", + "clientSecret": "testSecret", + "scopes": "ascope", + "tokenURL": "https://localhost:9999", + "headerName": "authorization", + "authStyle": "1", + "pathFilter": "/api/v1/users/.*", + } + + log := logger.NewLogger("oauth2clientcredentials.test") + oauth2clientcredentialsMiddleware, ok := NewOAuth2ClientCredentialsMiddleware(log).(*Middleware) + require.True(t, ok) + oauth2clientcredentialsMiddleware.SetTokenProvider(mockTokenProvider) + handler, err := oauth2clientcredentialsMiddleware.GetHandler(t.Context(), metadata) + require.NoError(t, err) + + // pathFilter should match + r := httptest.NewRequest(http.MethodGet, "http://dapr.io/api/v1/users/123", nil) + w := httptest.NewRecorder() + handler(http.HandlerFunc(mockedRequestHandler)).ServeHTTP(w, r) + + assert.Equal(t, "Bearer abcd", r.Header.Get("authorization")) + + // pathFilter should not match + r = httptest.NewRequest(http.MethodGet, "http://dapr.io/api/v1/tokens/123", nil) + w = httptest.NewRecorder() + handler(http.HandlerFunc(mockedRequestHandler)).ServeHTTP(w, r) + + assert.Equal(t, "", r.Header.Get("authorization")) +} diff --git a/pubsub/azure/servicebus/queues/servicebus.go b/pubsub/azure/servicebus/queues/servicebus.go index 4af688e3c5..342a4e227e 100644 --- a/pubsub/azure/servicebus/queues/servicebus.go +++ b/pubsub/azure/servicebus/queues/servicebus.go @@ -57,7 +57,7 @@ func (a *azureServiceBus) Init(_ context.Context, metadata pubsub.Metadata) (err return err } - a.client, err = impl.NewClient(a.metadata, metadata.Properties) + a.client, err = impl.NewClient(a.metadata, metadata.Properties, a.logger) if err != nil { return err } diff --git a/pubsub/azure/servicebus/topics/servicebus.go b/pubsub/azure/servicebus/topics/servicebus.go index 2e6ceed6e3..1f83add614 100644 --- a/pubsub/azure/servicebus/topics/servicebus.go +++ b/pubsub/azure/servicebus/topics/servicebus.go @@ -58,7 +58,7 @@ func (a *azureServiceBus) Init(_ context.Context, metadata pubsub.Metadata) (err return err } - a.client, err = impl.NewClient(a.metadata, metadata.Properties) + a.client, err = impl.NewClient(a.metadata, metadata.Properties, a.logger) if err != nil { return err } diff --git a/pubsub/pubsub.go b/pubsub/pubsub.go index f61261108e..47377d65e2 100644 --- a/pubsub/pubsub.go +++ b/pubsub/pubsub.go @@ -22,6 +22,8 @@ import ( "github.com/dapr/components-contrib/metadata" ) +var ErrGracefulShutdown = errors.New("pubsub shutdown") + // PubSub is the interface for message buses. type PubSub interface { metadata.ComponentWithMetadata diff --git a/pubsub/pulsar/metadata.go b/pubsub/pulsar/metadata.go index b3f54c1336..c373005425 100644 --- a/pubsub/pulsar/metadata.go +++ b/pubsub/pulsar/metadata.go @@ -39,6 +39,7 @@ type pulsarMetadata struct { ReceiverQueueSize int `mapstructure:"receiverQueueSize"` SubscriptionType string `mapstructure:"subscribeType"` SubscriptionInitialPosition string `mapstructure:"subscribeInitialPosition"` + ReplicateSubscriptionState bool `mapstructure:"replicateSubscriptionState"` SubscriptionMode string `mapstructure:"subscribeMode"` Token string `mapstructure:"token"` oauth2.ClientCredentialsMetadata `mapstructure:",squash"` diff --git a/pubsub/pulsar/metadata.yaml b/pubsub/pulsar/metadata.yaml index 8277583055..72c5fd5a30 100644 --- a/pubsub/pulsar/metadata.yaml +++ b/pubsub/pulsar/metadata.yaml @@ -202,6 +202,17 @@ metadata: url: title: "Pulsar SubscriptionInitialPosition" url: "https://pkg.go.dev/github.com/apache/pulsar-client-go/pulsar#SubscriptionInitialPosition" + - name: replicateSubscriptionState + type: bool + description: | + Enable replication of subscription state across geo-replicated Pulsar clusters. + When enabled, subscription state (such as cursor positions and acknowledgments) will be replicated to other clusters in a geo-replicated setup. + This is useful for maintaining subscription consistency during cluster failovers. + default: 'false' + example: '"true", "false"' + url: + title: "Pulsar Geo-Replication" + url: "https://pulsar.apache.org/docs/administration-geo/" - name: subscribeMode type: string description: | @@ -210,4 +221,4 @@ metadata: example: '"durable"' url: title: "Pulsar SubscriptionMode" - url: "https://pkg.go.dev/github.com/apache/pulsar-client-go/pulsar#SubscriptionMode" \ No newline at end of file + url: "https://pkg.go.dev/github.com/apache/pulsar-client-go/pulsar#SubscriptionMode" diff --git a/pubsub/pulsar/pulsar.go b/pubsub/pulsar/pulsar.go index 56c1d9b322..1ce44dc209 100644 --- a/pubsub/pulsar/pulsar.go +++ b/pubsub/pulsar/pulsar.go @@ -509,6 +509,7 @@ func (p *Pulsar) Subscribe(ctx context.Context, req pubsub.SubscribeRequest, han MessageChannel: channel, NackRedeliveryDelay: p.metadata.RedeliveryDelay, ReceiverQueueSize: p.metadata.ReceiverQueueSize, + ReplicateSubscriptionState: p.metadata.ReplicateSubscriptionState, } // Handle KeySharedPolicy for key_shared subscription type diff --git a/pubsub/pulsar/pulsar_test.go b/pubsub/pulsar/pulsar_test.go index 7ade8c9fc9..eff822515b 100644 --- a/pubsub/pulsar/pulsar_test.go +++ b/pubsub/pulsar/pulsar_test.go @@ -605,6 +605,48 @@ func TestEncryptionKeys(t *testing.T) { }) } +func TestParsePulsarMetadataReplicateSubscriptionState(t *testing.T) { + tt := []struct { + name string + replicateSubscriptionState string + expected bool + }{ + { + name: "test replicateSubscriptionState true", + replicateSubscriptionState: "true", + expected: true, + }, + { + name: "test replicateSubscriptionState false", + replicateSubscriptionState: "false", + expected: false, + }, + { + name: "test replicateSubscriptionState empty (defaults to false)", + replicateSubscriptionState: "", + expected: false, + }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + m := pubsub.Metadata{} + m.Properties = map[string]string{ + "host": "a", + } + + if tc.replicateSubscriptionState != "" { + m.Properties["replicateSubscriptionState"] = tc.replicateSubscriptionState + } + + meta, err := parsePulsarMetadata(m) + + require.NoError(t, err) + assert.Equal(t, tc.expected, meta.ReplicateSubscriptionState) + }) + } +} + func TestSanitiseURL(t *testing.T) { tests := []struct { name string diff --git a/pubsub/redis/metadata.yaml b/pubsub/redis/metadata.yaml index ed852f2076..5e0684d1e1 100644 --- a/pubsub/redis/metadata.yaml +++ b/pubsub/redis/metadata.yaml @@ -31,6 +31,30 @@ authenticationProfiles: secret reference example: "KeFg23!" default: "" + - name: sentinelUsername + type: string + required: false + description: | + Username for Redis Sentinel. Applicable only when "failover" is true, and + Redis Sentinel has authentication enabled. Defaults to empty. + example: "my-sentinel-username" + default: "" + url: + title: "Redis Sentinel authentication documentation" + url: "https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/#configuring-sentinel-instances-with-authentication" + - name: sentinelPassword + type: string + required: false + sensitive: true + description: | + Password for Redis Sentinel. Applicable only when "failover" is true, and + Redis Sentinel has authentication enabled. Use secretKeyRef for + secret reference. Defaults to empty. + example: "KeFg23!" + default: "" + url: + title: "Redis Sentinel authentication documentation" + url: "https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/#configuring-sentinel-instances-with-authentication" metadata: - name: redisHost required: true @@ -186,7 +210,7 @@ metadata: - name: sentinelMasterName required: false description: The sentinel master name. See Redis Sentinel Documentation. - example: "127.0.0.1:6379" + example: "mymaster" type: string - name: maxLenApprox required: false diff --git a/state/alicloud/tablestore/mock_client.go b/state/alicloud/tablestore/mock_client.go index f29678525f..4718c2d830 100644 --- a/state/alicloud/tablestore/mock_client.go +++ b/state/alicloud/tablestore/mock_client.go @@ -16,6 +16,7 @@ package tablestore import ( "bytes" "encoding/binary" + "sync" "github.com/aliyun/aliyun-tablestore-go-sdk/tablestore" ) @@ -24,6 +25,7 @@ type mockClient struct { tablestore.TableStoreClient data map[string][]byte + mu sync.RWMutex } func (m *mockClient) DeleteRow(request *tablestore.DeleteRowRequest) (*tablestore.DeleteRowResponse, error) { @@ -36,7 +38,9 @@ func (m *mockClient) DeleteRow(request *tablestore.DeleteRowRequest) (*tablestor } } + m.mu.Lock() delete(m.data, key) + m.mu.Unlock() return nil, nil } @@ -51,7 +55,9 @@ func (m *mockClient) GetRow(request *tablestore.GetRowRequest) (*tablestore.GetR } } + m.mu.RLock() val := m.data[key] + m.mu.RUnlock() resp := &tablestore.GetRowResponse{ Columns: []*tablestore.AttributeColumn{{ @@ -87,7 +93,9 @@ func (m *mockClient) UpdateRow(req *tablestore.UpdateRowRequest) (*tablestore.Up } } + m.mu.Lock() m.data[key] = val + m.mu.Unlock() return nil, nil } @@ -97,6 +105,7 @@ func (m *mockClient) BatchGetRow(request *tablestore.BatchGetRowRequest) (*table TableToRowsResult: map[string][]tablestore.RowResult{}, } + m.mu.RLock() for _, criteria := range request.MultiRowQueryCriteria { tableRes := resp.TableToRowsResult[criteria.TableName] if tableRes == nil { @@ -136,12 +145,14 @@ func (m *mockClient) BatchGetRow(request *tablestore.BatchGetRowRequest) (*table } } } + m.mu.RUnlock() return resp, nil } func (m *mockClient) BatchWriteRow(request *tablestore.BatchWriteRowRequest) (*tablestore.BatchWriteRowResponse, error) { resp := &tablestore.BatchWriteRowResponse{} + m.mu.Lock() for _, changes := range request.RowChangesGroupByTable { for _, change := range changes { switch inst := change.(type) { @@ -174,6 +185,7 @@ func (m *mockClient) BatchWriteRow(request *tablestore.BatchWriteRowRequest) (*t } } } + m.mu.Unlock() return resp, nil } diff --git a/state/oracledatabase/oracledatabaseaccess.go b/state/oracledatabase/oracledatabaseaccess.go index 9a62ce6d4d..fdd5374a59 100644 --- a/state/oracledatabase/oracledatabaseaccess.go +++ b/state/oracledatabase/oracledatabaseaccess.go @@ -514,8 +514,16 @@ func (o *oracleDatabaseAccess) ensureStateTable(stateTableName string) error { } func tableExists(db *sql.DB, tableName string) (bool, error) { - var tblCount int32 - err := db.QueryRow("SELECT count(table_name) tbl_count FROM user_tables WHERE table_name = upper(:tablename)", tableName).Scan(&tblCount) - exists := tblCount > 0 - return exists, err + //nolint:gosec + query := fmt.Sprintf("SELECT 1 FROM %s WHERE ROWNUM = 1", tableName) + + var dummy int + err := db.QueryRow(query).Scan(&dummy) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return true, nil // Table exists but is empty + } + return false, nil // Likely a table does not exist error + } + return true, nil } diff --git a/state/redis/metadata.yaml b/state/redis/metadata.yaml index 79898a759b..cab536678f 100644 --- a/state/redis/metadata.yaml +++ b/state/redis/metadata.yaml @@ -36,6 +36,30 @@ authenticationProfiles: secret reference example: "KeFg23!" default: "" + - name: sentinelUsername + type: string + required: false + description: | + Username for Redis Sentinel. Applicable only when "failover" is true, and + Redis Sentinel has authentication enabled. Defaults to empty. + example: "my-sentinel-username" + default: "" + url: + title: "Redis Sentinel authentication documentation" + url: "https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/#configuring-sentinel-instances-with-authentication" + - name: sentinelPassword + type: string + required: false + sensitive: true + description: | + Password for Redis Sentinel. Applicable only when "failover" is true, and + Redis Sentinel has authentication enabled. Use secretKeyRef for + secret reference. Defaults to empty. + example: "KeFg23!" + default: "" + url: + title: "Redis Sentinel authentication documentation" + url: "https://redis.io/docs/latest/operate/oss_and_stack/management/sentinel/#configuring-sentinel-instances-with-authentication" metadata: - name: redisHost required: true @@ -72,7 +96,7 @@ metadata: required: false description: | The Redis sentinel master name. Required when "failover" is enabled. - example: "127.0.0.1:6379" + example: "mymaster" type: string url: title: "Redis Sentinel documentation" diff --git a/state/sqlserver/sqlserver.go b/state/sqlserver/sqlserver.go index 35e2f7ed20..f607d47af1 100644 --- a/state/sqlserver/sqlserver.go +++ b/state/sqlserver/sqlserver.go @@ -16,6 +16,7 @@ package sqlserver import ( "context" "database/sql" + "encoding/base64" "encoding/hex" "encoding/json" "errors" @@ -287,8 +288,15 @@ func (s *SQLServer) Get(ctx context.Context, req *state.GetRequest) (*state.GetR } } + bytes, err := base64.StdEncoding.DecodeString(data) + if err != nil { + s.logger. + WithFields(map[string]any{"error": err}). + Debug("error decoding base64 data. Fallback to []byte") + bytes = []byte(data) + } return &state.GetResponse{ - Data: []byte(data), + Data: bytes, ETag: ptr.Of(etag), Metadata: metadata, }, nil @@ -305,16 +313,23 @@ type dbExecutor interface { } func (s *SQLServer) executeSet(ctx context.Context, db dbExecutor, req *state.SetRequest) error { - var err error - var bytes []byte - bytes, err = utils.Marshal(req.Value, json.Marshal) - if err != nil { - return err + var reqValue string + + bytes, ok := req.Value.([]byte) + if !ok { + bt, err := json.Marshal(req.Value) + if err != nil { + return err + } + reqValue = string(bt) + } else { + reqValue = base64.StdEncoding.EncodeToString(bytes) } + etag := sql.Named(rowVersionColumnName, nil) if req.HasETag() { var b []byte - b, err = hex.DecodeString(*req.ETag) + b, err := hex.DecodeString(*req.ETag) if err != nil { return state.NewETagError(state.ETagInvalid, err) } @@ -327,13 +342,14 @@ func (s *SQLServer) executeSet(ctx context.Context, db dbExecutor, req *state.Se } var res sql.Result + var err error if req.Options.Concurrency == state.FirstWrite { res, err = db.ExecContext(ctx, s.upsertCommand, sql.Named(keyColumnName, req.Key), - sql.Named("Data", string(bytes)), etag, + sql.Named("Data", reqValue), etag, sql.Named("FirstWrite", 1), sql.Named("TTL", ttl)) } else { res, err = db.ExecContext(ctx, s.upsertCommand, sql.Named(keyColumnName, req.Key), - sql.Named("Data", string(bytes)), etag, + sql.Named("Data", reqValue), etag, sql.Named("FirstWrite", 0), sql.Named("TTL", ttl)) } diff --git a/state/sqlserver/sqlserver_integration_test.go b/state/sqlserver/sqlserver_integration_test.go index 7af6220476..e317128a40 100644 --- a/state/sqlserver/sqlserver_integration_test.go +++ b/state/sqlserver/sqlserver_integration_test.go @@ -30,10 +30,12 @@ import ( "testing" "time" - uuid "github.com/google/uuid" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" + "github.com/dapr/components-contrib/common/proto/state/sqlserver" "github.com/dapr/components-contrib/metadata" "github.com/dapr/components-contrib/state" "github.com/dapr/kit/logger" @@ -42,7 +44,7 @@ import ( const ( // connectionStringEnvKey defines the key containing the integration test connection string // To use docker, server=localhost;user id=sa;password=Pass@Word1;port=1433; - // To use Azure SQL, server=.database.windows.net;user id=;port=1433;password=;database=dapr_test;. + // To use Azure SQL, server=.database.windows.net;User id=;port=1433;password=;database=dapr_test;. connectionStringEnvKey = "DAPR_TEST_SQL_CONNSTRING" usersTableName = "Users" beverageTea = "tea" @@ -77,6 +79,7 @@ func TestIntegrationCases(t *testing.T) { t.Run("Multi operations", testMultiOperations) t.Run("Insert and Update Set Record Dates", testInsertAndUpdateSetRecordDates) t.Run("Multiple initializations", testMultipleInitializations) + t.Run("Should preserve byte data when not base64 encoded", testNonBase64ByteData) // Run concurrent set tests 10 times const executions = 10 @@ -112,6 +115,9 @@ func createMetadata(schema string, kt KeyType, indexedProperties string) state.M // Ensure the database is running // For docker, use: docker run --name sqlserver -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=Pass@Word1" -p 1433:1433 -d mcr.microsoft.com/mssql/server:2019-GA-ubuntu-16.04. +// For azure-sql-edge use: +// docker volume create sqlvolume +// docker run --name sqlserver -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=Pass@Word1" -e "MSSQL_PID=Developer" -e "MSSQL_AGENT_ENABLED=TRUE" -e "MSSQL_COLLATION=SQL_Latin1_General_CP1_CI_AS" -e "MSSQL_LCID=1033" -p 1433:1433 -v sqlvolume:/var/opt/mssql -d mcr.microsoft.com/azure-sql-edge:latest func getTestStore(t *testing.T, indexedProperties string) *SQLServer { return getTestStoreWithKeyType(t, StringKeyType, indexedProperties) } @@ -597,3 +603,23 @@ func testMultipleInitializations(t *testing.T) { }) } } + +func testNonBase64ByteData(t *testing.T) { + t.Run("Set And Get", func(t *testing.T) { + store := getTestStore(t, "") + request := &sqlserver.TestEvent{ + EventId: -1, + } + requestBytes, err := proto.Marshal(request) + require.NoError(t, err) + require.NoError(t, store.Set(t.Context(), &state.SetRequest{Key: "1", Value: requestBytes})) + resp, err := store.Get(t.Context(), &state.GetRequest{Key: "1"}) + require.NoError(t, err) + + response := &sqlserver.TestEvent{} + err = proto.Unmarshal(resp.Data, response) + require.NoError(t, err) + + assert.EqualValues(t, request.GetEventId(), response.GetEventId()) + }) +} From e1371c6069d4719ceeb6548309ee97e50a349447 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Mon, 11 Aug 2025 10:16:55 -0500 Subject: [PATCH 29/32] revert: checkout lock + crypto dir from main since in diff pr now Signed-off-by: Samantha Coyle --- crypto/azure/keyvault/metadata.yaml | 28 ---- crypto/jwks/metadata.yaml | 35 ----- crypto/kubernetes/secrets/metadata.yaml | 25 ---- crypto/localstorage/metadata.yaml | 18 --- lock/redis/metadata.yaml | 183 ------------------------ 5 files changed, 289 deletions(-) delete mode 100644 crypto/azure/keyvault/metadata.yaml delete mode 100644 crypto/jwks/metadata.yaml delete mode 100644 crypto/kubernetes/secrets/metadata.yaml delete mode 100644 crypto/localstorage/metadata.yaml delete mode 100644 lock/redis/metadata.yaml diff --git a/crypto/azure/keyvault/metadata.yaml b/crypto/azure/keyvault/metadata.yaml deleted file mode 100644 index 390500d579..0000000000 --- a/crypto/azure/keyvault/metadata.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: cryptography -name: azure.keyvault -version: v1 -status: alpha -title: "Azure Key Vault" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-cryptography/azure-key-vault/ -builtinAuthenticationProfiles: - - name: "azuread" -metadata: - - name: vaultName - type: string - required: true - description: | - Name of the Azure Key Vault resource. - example: "my-key-vault" - - name: requestTimeout - type: duration - required: false - description: | - Timeout for network requests, as a Go duration string. - example: "30s" - default: "30s" - - diff --git a/crypto/jwks/metadata.yaml b/crypto/jwks/metadata.yaml deleted file mode 100644 index 8ba1bdc11d..0000000000 --- a/crypto/jwks/metadata.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: cryptography -name: jwks -version: v1 -status: alpha -title: "JWKS" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-cryptography/json-web-key-sets/ -metadata: - - name: jwks - type: string - required: true - description: | - The JWKS to use. Can be one of: - - The actual JWKS as a JSON-encoded string (optionally encoded with Base64-standard). - - A URL to a HTTP(S) endpoint returning the JWKS. - - A path to a local file containing the JWKS. - example: '"https://example.com/.well-known/jwks.json"' - - name: requestTimeout - type: duration - required: false - description: | - Timeout for network requests, as a Go duration string. - example: "30s" - default: "30s" - - name: minRefreshInterval - type: duration - required: false - description: | - Minimum interval before the JWKS is refreshed, as a Go duration string. - Only applies when the JWKS is fetched from a HTTP(S) URL. - example: "10m" - default: "10m" diff --git a/crypto/kubernetes/secrets/metadata.yaml b/crypto/kubernetes/secrets/metadata.yaml deleted file mode 100644 index 1e3d9b5e9b..0000000000 --- a/crypto/kubernetes/secrets/metadata.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: cryptography -name: kubernetes.secrets -version: v1 -status: alpha -title: "Kubernetes Secrets" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-cryptography/kubernetes-secrets/ -metadata: - - name: defaultNamespace - type: string - required: false - description: | - Default namespace to retrieve secrets from. - If unset, the namespace must be specified for each key, as `namespace/secretName/key`. - example: '"default"' - - name: kubeconfigPath - type: string - required: false - description: | - Path to a kubeconfig file. - If empty, uses the default values. - example: '"/path/to/kubeconfig"' diff --git a/crypto/localstorage/metadata.yaml b/crypto/localstorage/metadata.yaml deleted file mode 100644 index e7841aefce..0000000000 --- a/crypto/localstorage/metadata.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: cryptography -name: localstorage -version: v1 -status: alpha -title: "Local Storage" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-cryptography/local-storage/ -metadata: - - name: path - type: string - required: true - description: | - Path to a local folder where keys are stored. - Keys are loaded from PEM or JSON (each containing an individual JWK) files from this folder. - example: '"/path/to/keys"' \ No newline at end of file diff --git a/lock/redis/metadata.yaml b/lock/redis/metadata.yaml deleted file mode 100644 index 41b713f0c9..0000000000 --- a/lock/redis/metadata.yaml +++ /dev/null @@ -1,183 +0,0 @@ -# yaml-language-server: $schema=../../component-metadata-schema.json -schemaVersion: v1 -type: lock -name: redis -version: v1 -status: alpha -title: "Redis Distributed Lock" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-locks/redis-lock/ -lock: - operations: - - name: tryLock - description: "Attempt to acquire a distributed lock" - - name: unlock - description: "Release a distributed lock" -authenticationProfiles: - - title: "Password Authentication" - description: | - Authenticate using Redis password. - metadata: - - name: redisUsername - required: false - description: "The Redis username" - example: '"redis-user"' - - name: redisPassword - required: false - sensitive: true - description: "The Redis password" - example: '"your-redis-password"' - - title: "Sentinel Authentication" - description: | - Authenticate using Redis Sentinel password. - metadata: - - name: sentinelUsername - required: false - description: "The Redis Sentinel username" - example: '"sentinel-user"' - - name: sentinelPassword - required: false - sensitive: true - description: "The Redis Sentinel password" - example: '"sentinel-password"' - - title: "TLS Authentication" - description: | - Authenticate using Redis TLS certificate. - metadata: - - name: clientCert - required: false - sensitive: true - description: "The Redis client certificate" - example: '"-----BEGIN CERTIFICATE-----\nXXX..."' - - name: clientKey - required: false - sensitive: true - description: "The Redis client key" - example: '"-----BEGIN PRIVATE KEY-----\nXXX..."' -metadata: - - name: redisHost - required: true - description: "The Redis host address" - example: '"localhost:6379"' - - name: redisPassword - required: false - sensitive: true - description: "The Redis password" - example: '"your-redis-password"' - - name: redisType - required: false - description: "The Redis type" - example: "node" - default: "node" - allowedValues: - - "node" - - "cluster" - - "sentinel" - - name: redisDB - required: false - description: "The Redis database number" - example: '0' - default: '0' - - name: redisMaxRetries - required: false - description: "Maximum Redis retries" - example: '3' - default: '3' - - name: redisMinRetryInterval - required: false - description: "Minimum Redis retry interval" - example: "8ms" - default: "8ms" - - name: redisMaxRetryInterval - required: false - description: "Maximum Redis retry interval" - example: "512ms" - default: "512ms" - - name: dialTimeout - required: false - description: "Dial timeout duration" - example: "5s" - default: "5s" - - name: readTimeout - required: false - description: "Read timeout duration" - example: "3s" - default: "3s" - - name: writeTimeout - required: false - description: "Write timeout duration" - example: "3s" - default: "3s" - - name: poolSize - required: false - description: "Connection pool size" - example: '10' - default: '10' - - name: poolTimeout - required: false - description: "Connection pool timeout" - example: "4s" - default: "4s" - - name: maxConnAge - required: false - description: "Maximum connection age" - example: "30m" - default: "30m" - - name: minIdleConns - required: false - description: "Minimum idle connections" - example: '0' - default: '0' - - name: idleTimeout - required: false - description: "Idle timeout duration" - example: "5m" - default: "5m" - - name: idleCheckFrequency - required: false - description: "Idle check frequency" - example: "1m" - default: "1m" - - name: maxRetries - required: false - description: "Maximum number of retries when attempting to acquire a lock" - example: '3' - default: '3' - - name: maxRetryBackoff - required: false - description: "Maximum backoff duration between retries" - example: "2s" - default: "2s" - - name: redeliverInterval - required: false - description: "Redeliver interval for re-attempting lock acquisition" - example: "15s" - default: "15s" - - name: processingTimeout - required: false - description: "Processing timeout for lock ownership" - example: "60s" - default: "60s" - - name: enableTLS - required: false - type: bool - description: "Whether to enable TLS encryption" - example: false - default: false - - name: useEntraID - required: false - type: bool - description: "Whether to use Entra ID for authentication" - example: false - default: false - - name: failover - required: false - type: bool - description: "Whether to enable failover mode (for Sentinel)" - example: false - default: false - - name: sentinelMasterName - required: false - description: "The Sentinel master name (used if redisType is 'sentinel')" - example: '"mymaster"' From 968cc2363fbe03ad0312dbc29f57f401ed60901e Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Mon, 11 Aug 2025 10:19:52 -0500 Subject: [PATCH 30/32] revert: checkout secretstore dir from main since in diff pr now Signed-off-by: Samantha Coyle --- conversation/echo/metadata.yaml | 2 +- .../alicloud/parameterstore/metadata.yaml | 45 ------------------- secretstores/huaweicloud/csms/csms.go | 10 ++--- secretstores/huaweicloud/csms/metadata.yaml | 35 --------------- secretstores/local/file/filestore.go | 6 +-- secretstores/local/file/metadata.yaml | 29 ------------ secretstores/tencentcloud/ssm/metadata.yaml | 40 ----------------- secretstores/tencentcloud/ssm/ssm.go | 9 ++-- 8 files changed, 13 insertions(+), 163 deletions(-) delete mode 100644 secretstores/alicloud/parameterstore/metadata.yaml delete mode 100644 secretstores/huaweicloud/csms/metadata.yaml delete mode 100644 secretstores/local/file/metadata.yaml delete mode 100644 secretstores/tencentcloud/ssm/metadata.yaml diff --git a/conversation/echo/metadata.yaml b/conversation/echo/metadata.yaml index 734bbb9197..d5cf348862 100644 --- a/conversation/echo/metadata.yaml +++ b/conversation/echo/metadata.yaml @@ -8,4 +8,4 @@ title: "Echo" urls: - title: Reference url: https://docs.dapr.io/reference/components-reference/supported-conversation/echo/ -metadata: \ No newline at end of file +metadata: [] diff --git a/secretstores/alicloud/parameterstore/metadata.yaml b/secretstores/alicloud/parameterstore/metadata.yaml deleted file mode 100644 index 72f4fc30ca..0000000000 --- a/secretstores/alicloud/parameterstore/metadata.yaml +++ /dev/null @@ -1,45 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: secretstores -name: alicloud.parameterstore -version: v1 -status: alpha -title: "AliCloud OSS Parameter Store" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-secret-stores/alicloud-oss-parameter-store/ -authenticationProfiles: - - title: "Access Key Authentication" - description: "Authenticate using AliCloud access key and secret." - metadata: - - name: regionId - type: string - required: true - description: The AliCloud region ID. - example: "cn-hangzhou" - - name: accessKeyId - type: string - required: true - description: The AliCloud access key ID. - example: "your-access-key-id" - - name: accessKeySecret - type: string - required: true - description: The AliCloud access key secret. - example: "your-access-key-secret" - - name: securityToken - type: string - required: false - description: The AliCloud security token for temporary credentials. - example: "your-security-token" -metadata: - - name: version_id - type: string - required: false - description: The version ID of the parameter to retrieve. If not specified, the latest version is used. - example: "1" - - name: path - type: string - required: false - description: The path prefix for bulk operations. If not specified, root path (/) is used. - example: "/myapp/" \ No newline at end of file diff --git a/secretstores/huaweicloud/csms/csms.go b/secretstores/huaweicloud/csms/csms.go index e318e4a5ea..a0046b5f84 100644 --- a/secretstores/huaweicloud/csms/csms.go +++ b/secretstores/huaweicloud/csms/csms.go @@ -48,9 +48,9 @@ type csmsSecretStore struct { } type CsmsSecretStoreMetadata struct { - Region string `json:"region"` - AccessKey string `json:"accessKey"` - SecretAccessKey string `json:"secretAccessKey"` + Region string + AccessKey string + SecretAccessKey string } // NewHuaweiCsmsSecretStore returns a new Huawei csms secret store. @@ -114,7 +114,7 @@ func (c *csmsSecretStore) BulkGetSecret(ctx context.Context, req secretstores.Bu secret, err := c.GetSecret(ctx, secretstores.GetSecretRequest{ Name: secretName, Metadata: map[string]string{ - versionID: latestVersion, // TODO: make this configurable + versionID: latestVersion, }, }) if err != nil { @@ -130,7 +130,7 @@ func (c *csmsSecretStore) BulkGetSecret(ctx context.Context, req secretstores.Bu // Get all secret names recursively. func (c *csmsSecretStore) getSecretNames(ctx context.Context, marker *string) ([]string, error) { request := &model.ListSecretsRequest{} - limit := pageLimit // TODO: make this configurable + limit := pageLimit request.Limit = &limit request.Marker = marker diff --git a/secretstores/huaweicloud/csms/metadata.yaml b/secretstores/huaweicloud/csms/metadata.yaml deleted file mode 100644 index 8d84931967..0000000000 --- a/secretstores/huaweicloud/csms/metadata.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: secretstores -name: huaweicloud.csms -version: v1 -status: alpha -title: "HuaweiCloud CSMS" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-secret-stores/huaweicloud-csms/ -authenticationProfiles: - - title: "Access Key Authentication" - description: "Authenticate using HuaweiCloud access key and secret." - metadata: - - name: region - type: string - required: true - description: The HuaweiCloud region. - example: "cn-north-4" - - name: accessKey - type: string - required: true - description: The HuaweiCloud access key. - example: "your-access-key" - - name: secretAccessKey - type: string - required: true - description: The HuaweiCloud secret access key. - example: "your-secret-access-key" -metadata: - - name: version_id - type: string - required: false - description: The version ID of the secret to retrieve. If not specified, the latest version is used. - example: "1" \ No newline at end of file diff --git a/secretstores/local/file/filestore.go b/secretstores/local/file/filestore.go index a75717a655..27bc71cfbe 100644 --- a/secretstores/local/file/filestore.go +++ b/secretstores/local/file/filestore.go @@ -31,9 +31,9 @@ import ( ) type localSecretStoreMetaData struct { - SecretsFile string `json:"secretsFile"` - NestedSeparator string `json:"nestedSeparator"` - MultiValued bool `json:"multiValued"` + SecretsFile string + NestedSeparator string + MultiValued bool } var _ secretstores.SecretStore = (*localSecretStore)(nil) diff --git a/secretstores/local/file/metadata.yaml b/secretstores/local/file/metadata.yaml deleted file mode 100644 index a2586be84d..0000000000 --- a/secretstores/local/file/metadata.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: secretstores -name: local.file -version: v1 -status: stable -title: "Local File Secret Store" -description: "Read secrets from a local JSON file for local development." -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-secret-stores/file-secret-store/ -metadata: - - name: secretsFile - type: string - required: true - description: Path to the JSON file containing secrets. - example: "secrets.json" - - name: nestedSeparator - type: string - required: false - description: Separator used for nested keys in the JSON file. - example: ":" - default: ":" - - name: multiValued - type: bool - required: false - description: If true, enables multiple key-values per secret feature. - example: false - default: false \ No newline at end of file diff --git a/secretstores/tencentcloud/ssm/metadata.yaml b/secretstores/tencentcloud/ssm/metadata.yaml deleted file mode 100644 index 1194b7cdc8..0000000000 --- a/secretstores/tencentcloud/ssm/metadata.yaml +++ /dev/null @@ -1,40 +0,0 @@ -# yaml-language-server: $schema=../../../component-metadata-schema.json -schemaVersion: v1 -type: secretstores -name: tencentcloud.ssm -version: v1 -status: alpha -title: "TencentCloud Secret Manager" -urls: - - title: Reference - url: https://docs.dapr.io/reference/components-reference/supported-secret-stores/ -authenticationProfiles: - - title: "Secret Key Authentication" - description: "Authenticate using TencentCloud secret ID and key." - metadata: - - name: secretId - type: string - required: true - description: The TencentCloud secret ID. - example: "your-secret-id" - - name: secretKey - type: string - required: true - description: The TencentCloud secret key. - example: "your-secret-key" - - name: token - type: string - required: false - description: The TencentCloud temporary token for temporary credentials. - example: "your-token" - - name: region - type: string - required: true - description: The TencentCloud region. - example: "ap-guangzhou" -metadata: - - name: VersionID - type: string - required: false - description: The version ID of the secret to retrieve. - example: "1" \ No newline at end of file diff --git a/secretstores/tencentcloud/ssm/ssm.go b/secretstores/tencentcloud/ssm/ssm.go index 42de9bb48b..192d195f67 100644 --- a/secretstores/tencentcloud/ssm/ssm.go +++ b/secretstores/tencentcloud/ssm/ssm.go @@ -30,7 +30,6 @@ import ( ) const ( - // TODO: lowercase these and add to metadata struct eventually VersionID = "VersionID" RequestID = "RequestID" ValueType = "SecretValueType" @@ -57,10 +56,10 @@ type ssmSecretStore struct { } type SsmMetadata struct { - SecretID string `json:"secretId"` - SecretKey string `json:"secretKey"` - Token string `json:"token"` - Region string `json:"region"` + SecretID string + SecretKey string + Token string + Region string } // NewSSM returns a new TencentCloud ssm secret store. From c0213e2e1f6e2acaa04bde8fe795ca25aa187cc0 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Mon, 11 Aug 2025 11:36:55 -0500 Subject: [PATCH 31/32] fix: clean up other changes moved to other prs Signed-off-by: Samantha Coyle --- tests/certification/embedded/components.go | 2 +- tests/conformance/{cryptography => crypto}/crypto.go | 0 tests/conformance/{cryptography => crypto}/helpers.go | 0 tests/conformance/crypto_test.go | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) rename tests/conformance/{cryptography => crypto}/crypto.go (100%) rename tests/conformance/{cryptography => crypto}/helpers.go (100%) diff --git a/tests/certification/embedded/components.go b/tests/certification/embedded/components.go index 4f2c7f3adc..9a165099ab 100644 --- a/tests/certification/embedded/components.go +++ b/tests/certification/embedded/components.go @@ -18,7 +18,7 @@ import ( "github.com/dapr/kit/logger" // Name resolutions. - nrConsul "github.com/dapr/components-contrib/nameresolution/hashicorp/consul" + nrConsul "github.com/dapr/components-contrib/nameresolution/consul" nrKubernetes "github.com/dapr/components-contrib/nameresolution/kubernetes" nrMdns "github.com/dapr/components-contrib/nameresolution/mdns" diff --git a/tests/conformance/cryptography/crypto.go b/tests/conformance/crypto/crypto.go similarity index 100% rename from tests/conformance/cryptography/crypto.go rename to tests/conformance/crypto/crypto.go diff --git a/tests/conformance/cryptography/helpers.go b/tests/conformance/crypto/helpers.go similarity index 100% rename from tests/conformance/cryptography/helpers.go rename to tests/conformance/crypto/helpers.go diff --git a/tests/conformance/crypto_test.go b/tests/conformance/crypto_test.go index 8447d4e292..749c6beb5a 100644 --- a/tests/conformance/crypto_test.go +++ b/tests/conformance/crypto_test.go @@ -26,7 +26,7 @@ import ( cr_azurekeyvault "github.com/dapr/components-contrib/crypto/azure/keyvault" cr_jwks "github.com/dapr/components-contrib/crypto/jwks" cr_localstorage "github.com/dapr/components-contrib/crypto/localstorage" - conf_crypto "github.com/dapr/components-contrib/tests/conformance/cryptography" + conf_crypto "github.com/dapr/components-contrib/tests/conformance/crypto" ) func TestCryptoConformance(t *testing.T) { From 81234297ca129058516e28ce31478e898a5e4476 Mon Sep 17 00:00:00 2001 From: Samantha Coyle Date: Mon, 11 Aug 2025 11:54:03 -0500 Subject: [PATCH 32/32] style: few fixes after clean up on pr Signed-off-by: Samantha Coyle --- .../cmd/cmd-check-component-registrations.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.build-tools/cmd/cmd-check-component-registrations.go b/.build-tools/cmd/cmd-check-component-registrations.go index d1808c09f9..42610e4a3b 100644 --- a/.build-tools/cmd/cmd-check-component-registrations.go +++ b/.build-tools/cmd/cmd-check-component-registrations.go @@ -104,7 +104,7 @@ func checkBindingComponents() { fmt.Println("\nChecking bindings components...") // ignore servicebus.queues as runtime has an alias on this so we're checking for servicebusqueues ignoreDaprComponents := []string{"mqtt3", "azure.servicebus.queues", "postgresql"} - ignoreContribComponents := []string{} //[]string{"postgresql"} + ignoreContribComponents := []string{} checkComponents("bindings", ignoreDaprComponents, ignoreContribComponents) } @@ -123,7 +123,7 @@ func checkCryptographyComponents() { fmt.Println("\nChecking cryptography components...") // below is not actually a component. Cryptography section in contrib needs quite a bit of clean up/organization to clean this up so I don't have to "ignore" it. ignoreContribComponents := []string{"pubkey_cache"} - // TODO: in future rm the aliases with the dapr prefix in runtime to clean this up! + // We ignore the dapr prefixes here bc they are captured properly without the dapr prefix. ignoreDaprComponents := []string{"dapr.localstorage", "dapr.kubernetes.secrets", "dapr.jwks"} // TODO: in future update this to cryptography once we have a cryptography component in contrib and not crypto components checkComponents("crypto", ignoreDaprComponents, ignoreContribComponents) @@ -187,7 +187,7 @@ func checkComponents(componentType string, ignoreDaprComponents []string, ignore contribComponents = filteredContribComponents // Apply vendor prefix mapping and deduplication to both lists. - // This removes things like the CSP prefixing. + // This removes things like the CSP and/or vendor prefixing. mappedContribComponents := mapAndDeduplicateComponents(contribComponents) mappedDaprComponents := mapAndDeduplicateComponents(daprComponents) @@ -210,6 +210,8 @@ func checkComponents(componentType string, ignoreDaprComponents []string, ignore reportResults(missingRegistrations, missingBuildTags, missingMetadata, componentType) } +// getRegistryPath is needed to get the correct registry file for the component, +// and middleware components are nested under a specific http dir, so we must handle this case. func getRegistryPath(componentType string) string { if componentType == "middleware" { return fmt.Sprintf("../dapr/pkg/components/%s/http/registry.go", componentType) @@ -275,7 +277,8 @@ func findComponentsInBothRepos(componentType string, ignoreContribComponents []s // Find all components in components-contrib, excluding utility files // Configure exclude list based on component type var excludeFiles []string - // keeping this here for now since not all components exclude files... will make func param if needed. + // we have to exclude certain files that match the grep, but are not components. + // In future, this can be cleaned up if files are moved to proper pkg like directories. switch componentType { case "state": excludeFiles = []string{"--exclude=errors.go", "--exclude=bulk.go", "--exclude=query.go"} @@ -291,7 +294,6 @@ func findComponentsInBothRepos(componentType string, ignoreContribComponents []s excludeFiles = []string{} } - // Build the grep command with dynamic excludes grepArgs := []string{"-rl", "--include=*.go"} grepArgs = append(grepArgs, excludeFiles...) grepArgs = append(grepArgs, "func New", componentType) @@ -306,6 +308,7 @@ func findComponentsInBothRepos(componentType string, ignoreContribComponents []s var registeredOutput []byte if componentType != "bindings" { var searchPattern string + // bc middleware components are nested under a specific http dir, we must handle this case. if componentType == "middleware" { searchPattern = "../dapr/cmd/daprd/components/middleware_http_*.go" } else { @@ -410,12 +413,14 @@ func checkComponentIsActuallyRegisteredInFile(contrib, registrationFile string) func checkBuildTag(contrib, componentType string) error { compFileName := getRegistrationFileName(contrib, componentType) - // Check for "go:build allcomponents" (with or without additional conditions) + // Check for "go:build allcomponents" buildTagCmd := exec.Command("grep", "-q", "allcomponents", compFileName) _, err := buildTagCmd.Output() if err != nil { return fmt.Errorf("build tag for 'allcomponents' not found in %s", compFileName) } + + // TODO: in future, add check for stable components return nil } @@ -448,7 +453,6 @@ func normalizeComponentName(contrib string) string { // Handle 3+ part names (vendor.component.version) if len(parts) >= 3 { - // Check if first part is a vendor prefix if slices.Contains(vendorPrefixes, parts[0]) { // Check if last part is a version suffix if slices.Contains(versionSuffixes, parts[len(parts)-1]) {