Use net/url to build resource URLs such that proper escaping is done

This commit is contained in:
Pieter Noordhuis 2024-10-17 17:30:48 +02:00
parent 7bf2ec378c
commit 7658dbdff2
No known key found for this signature in database
GPG Key ID: 12ACCCC104CF2930
12 changed files with 63 additions and 27 deletions

View File

@ -2,6 +2,7 @@ package mutator
import ( import (
"context" "context"
"net/url"
"strconv" "strconv"
"strings" "strings"
@ -30,12 +31,17 @@ func (m *initializeURLs) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagn
return diag.FromErr(err) return diag.FromErr(err)
} }
orgId := strconv.FormatInt(workspaceId, 10) orgId := strconv.FormatInt(workspaceId, 10)
urlPrefix := b.WorkspaceClient().Config.CanonicalHostName() + "/" host := b.WorkspaceClient().Config.CanonicalHostName()
initializeForWorkspace(b, orgId, urlPrefix) initializeForWorkspace(b, orgId, host)
return nil return nil
} }
func initializeForWorkspace(b *bundle.Bundle, orgId string, urlPrefix string) { func initializeForWorkspace(b *bundle.Bundle, orgId string, host string) error {
baseURL, err := url.Parse(host)
if err != nil {
return err
}
// Add ?o=<workspace id> only if <workspace id> wasn't in the subdomain already. // Add ?o=<workspace id> only if <workspace id> wasn't in the subdomain already.
// The ?o= is needed when vanity URLs / legacy workspace URLs are used. // The ?o= is needed when vanity URLs / legacy workspace URLs are used.
// If it's not needed we prefer to leave it out since these URLs are rather // If it's not needed we prefer to leave it out since these URLs are rather
@ -43,14 +49,17 @@ func initializeForWorkspace(b *bundle.Bundle, orgId string, urlPrefix string) {
// //
// See https://docs.databricks.com/en/workspace/workspace-details.html for // See https://docs.databricks.com/en/workspace/workspace-details.html for
// further reading about the '?o=' suffix. // further reading about the '?o=' suffix.
urlSuffix := "" if !strings.Contains(baseURL.Hostname(), orgId) {
if !strings.Contains(urlPrefix, orgId) { values := baseURL.Query()
urlSuffix = "?o=" + orgId values.Add("o", orgId)
baseURL.RawQuery = values.Encode()
} }
for _, group := range b.Config.Resources.AllResources() { for _, group := range b.Config.Resources.AllResources() {
for _, r := range group.Resources { for _, r := range group.Resources {
r.InitializeURL(urlPrefix, urlSuffix) r.InitializeURL(*baseURL)
} }
} }
return nil
} }

View File

@ -93,7 +93,7 @@ func TestInitializeURLs(t *testing.T) {
"job1": "https://mycompany.databricks.com/jobs/1?o=123456", "job1": "https://mycompany.databricks.com/jobs/1?o=123456",
"pipeline1": "https://mycompany.databricks.com/pipelines/3?o=123456", "pipeline1": "https://mycompany.databricks.com/pipelines/3?o=123456",
"experiment1": "https://mycompany.databricks.com/ml/experiments/4?o=123456", "experiment1": "https://mycompany.databricks.com/ml/experiments/4?o=123456",
"model1": "https://mycompany.databricks.com/ml/models/a model uses its name for identifier?o=123456", "model1": "https://mycompany.databricks.com/ml/models/a%20model%20uses%20its%20name%20for%20identifier?o=123456",
"servingendpoint1": "https://mycompany.databricks.com/ml/endpoints/my_serving_endpoint?o=123456", "servingendpoint1": "https://mycompany.databricks.com/ml/endpoints/my_serving_endpoint?o=123456",
"registeredmodel1": "https://mycompany.databricks.com/explore/data/models/8?o=123456", "registeredmodel1": "https://mycompany.databricks.com/explore/data/models/8?o=123456",
"qualityMonitor1": "https://mycompany.databricks.com/explore/data/catalog/schema/qualityMonitor1?o=123456", "qualityMonitor1": "https://mycompany.databricks.com/explore/data/catalog/schema/qualityMonitor1?o=123456",

View File

@ -3,6 +3,7 @@ package config
import ( import (
"context" "context"
"fmt" "fmt"
"net/url"
"github.com/databricks/cli/bundle/config/resources" "github.com/databricks/cli/bundle/config/resources"
"github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go"
@ -38,7 +39,7 @@ type ConfigResource interface {
GetURL() string GetURL() string
// InitializeURL initializes the URL field of the resource. // InitializeURL initializes the URL field of the resource.
InitializeURL(urlPrefix string, urlSuffix string) InitializeURL(url.URL)
} }
// ResourceGroup represents a group of resources of the same type. // ResourceGroup represents a group of resources of the same type.

View File

@ -2,6 +2,8 @@ package resources
import ( import (
"context" "context"
"fmt"
"net/url"
"github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/log"
"github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go"
@ -39,11 +41,12 @@ func (s *Cluster) TerraformResourceName() string {
return "databricks_cluster" return "databricks_cluster"
} }
func (s *Cluster) InitializeURL(urlPrefix string, urlSuffix string) { func (s *Cluster) InitializeURL(baseURL url.URL) {
if s.ID == "" { if s.ID == "" {
return return
} }
s.URL = urlPrefix + "compute/clusters/" + s.ID + urlSuffix baseURL.Path = fmt.Sprintf("compute/clusters/%s", s.ID)
s.URL = baseURL.String()
} }
func (s *Cluster) GetName() string { func (s *Cluster) GetName() string {

View File

@ -2,6 +2,8 @@ package resources
import ( import (
"context" "context"
"fmt"
"net/url"
"strconv" "strconv"
"github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/log"
@ -46,11 +48,12 @@ func (j *Job) TerraformResourceName() string {
return "databricks_job" return "databricks_job"
} }
func (j *Job) InitializeURL(urlPrefix string, urlSuffix string) { func (j *Job) InitializeURL(baseURL url.URL) {
if j.ID == "" { if j.ID == "" {
return return
} }
j.URL = urlPrefix + "jobs/" + j.ID + urlSuffix baseURL.Path = fmt.Sprintf("jobs/%s", j.ID)
j.URL = baseURL.String()
} }
func (j *Job) GetName() string { func (j *Job) GetName() string {

View File

@ -2,6 +2,8 @@ package resources
import ( import (
"context" "context"
"fmt"
"net/url"
"github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/log"
"github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go"
@ -41,11 +43,12 @@ func (s *MlflowExperiment) TerraformResourceName() string {
return "databricks_mlflow_experiment" return "databricks_mlflow_experiment"
} }
func (s *MlflowExperiment) InitializeURL(urlPrefix string, urlSuffix string) { func (s *MlflowExperiment) InitializeURL(baseURL url.URL) {
if s.ID == "" { if s.ID == "" {
return return
} }
s.URL = urlPrefix + "ml/experiments/" + s.ID + urlSuffix baseURL.Path = fmt.Sprintf("ml/experiments/%s", s.ID)
s.URL = baseURL.String()
} }
func (s *MlflowExperiment) GetName() string { func (s *MlflowExperiment) GetName() string {

View File

@ -2,6 +2,8 @@ package resources
import ( import (
"context" "context"
"fmt"
"net/url"
"github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/log"
"github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go"
@ -41,11 +43,12 @@ func (s *MlflowModel) TerraformResourceName() string {
return "databricks_mlflow_model" return "databricks_mlflow_model"
} }
func (s *MlflowModel) InitializeURL(urlPrefix string, urlSuffix string) { func (s *MlflowModel) InitializeURL(baseURL url.URL) {
if s.ID == "" { if s.ID == "" {
return return
} }
s.URL = urlPrefix + "ml/models/" + s.ID + urlSuffix baseURL.Path = fmt.Sprintf("ml/models/%s", s.ID)
s.URL = baseURL.String()
} }
func (s *MlflowModel) GetName() string { func (s *MlflowModel) GetName() string {

View File

@ -2,6 +2,8 @@ package resources
import ( import (
"context" "context"
"fmt"
"net/url"
"github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/log"
"github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go"
@ -49,11 +51,12 @@ func (s *ModelServingEndpoint) TerraformResourceName() string {
return "databricks_model_serving" return "databricks_model_serving"
} }
func (s *ModelServingEndpoint) InitializeURL(urlPrefix string, urlSuffix string) { func (s *ModelServingEndpoint) InitializeURL(baseURL url.URL) {
if s.ID == "" { if s.ID == "" {
return return
} }
s.URL = urlPrefix + "ml/endpoints/" + s.ID + urlSuffix baseURL.Path = fmt.Sprintf("ml/endpoints/%s", s.ID)
s.URL = baseURL.String()
} }
func (s *ModelServingEndpoint) GetName() string { func (s *ModelServingEndpoint) GetName() string {

View File

@ -2,6 +2,8 @@ package resources
import ( import (
"context" "context"
"fmt"
"net/url"
"github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/log"
"github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go"
@ -41,11 +43,12 @@ func (p *Pipeline) TerraformResourceName() string {
return "databricks_pipeline" return "databricks_pipeline"
} }
func (p *Pipeline) InitializeURL(urlPrefix string, urlSuffix string) { func (p *Pipeline) InitializeURL(baseURL url.URL) {
if p.ID == "" { if p.ID == "" {
return return
} }
p.URL = urlPrefix + "pipelines/" + p.ID + urlSuffix baseURL.Path = fmt.Sprintf("pipelines/%s", p.ID)
p.URL = baseURL.String()
} }
func (p *Pipeline) GetName() string { func (p *Pipeline) GetName() string {

View File

@ -2,6 +2,8 @@ package resources
import ( import (
"context" "context"
"fmt"
"net/url"
"strings" "strings"
"github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/log"
@ -47,11 +49,12 @@ func (s *QualityMonitor) TerraformResourceName() string {
return "databricks_quality_monitor" return "databricks_quality_monitor"
} }
func (s *QualityMonitor) InitializeURL(urlPrefix string, urlSuffix string) { func (s *QualityMonitor) InitializeURL(baseURL url.URL) {
if s.TableName == "" { if s.TableName == "" {
return return
} }
s.URL = urlPrefix + "explore/data/" + strings.ReplaceAll(s.TableName, ".", "/") + urlSuffix baseURL.Path = fmt.Sprintf("explore/data/%s", strings.ReplaceAll(s.TableName, ".", "/"))
s.URL = baseURL.String()
} }
func (s *QualityMonitor) GetName() string { func (s *QualityMonitor) GetName() string {

View File

@ -2,6 +2,8 @@ package resources
import ( import (
"context" "context"
"fmt"
"net/url"
"strings" "strings"
"github.com/databricks/cli/libs/log" "github.com/databricks/cli/libs/log"
@ -51,11 +53,12 @@ func (s *RegisteredModel) TerraformResourceName() string {
return "databricks_registered_model" return "databricks_registered_model"
} }
func (s *RegisteredModel) InitializeURL(urlPrefix string, urlSuffix string) { func (s *RegisteredModel) InitializeURL(baseURL url.URL) {
if s.ID == "" { if s.ID == "" {
return return
} }
s.URL = urlPrefix + "explore/data/models/" + strings.ReplaceAll(s.ID, ".", "/") + urlSuffix baseURL.Path = fmt.Sprintf("explore/data/models/%s", strings.ReplaceAll(s.ID, ".", "/"))
s.URL = baseURL.String()
} }
func (s *RegisteredModel) GetName() string { func (s *RegisteredModel) GetName() string {

View File

@ -3,6 +3,7 @@ package resources
import ( import (
"context" "context"
"fmt" "fmt"
"net/url"
"strings" "strings"
"github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go"
@ -32,11 +33,12 @@ func (s *Schema) TerraformResourceName() string {
return "databricks_schema" return "databricks_schema"
} }
func (s *Schema) InitializeURL(urlPrefix string, urlSuffix string) { func (s *Schema) InitializeURL(baseURL url.URL) {
if s.ID == "" { if s.ID == "" {
return return
} }
s.URL = urlPrefix + "explore/data/" + strings.ReplaceAll(s.ID, ".", "/") + urlSuffix baseURL.Path = fmt.Sprintf("explore/data/%s", strings.ReplaceAll(s.ID, ".", "/"))
s.URL = baseURL.String()
} }
func (s *Schema) GetURL() string { func (s *Schema) GetURL() string {