Skip to content

Commit

Permalink
changes vmauth config generation
Browse files Browse the repository at this point in the history
adds `url_path_suffix`  for #245
use simple url_prefix instead of url_map in default case
  • Loading branch information
f41gh7 committed Jul 27, 2021
1 parent 21aa4ff commit 5dcd998
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 54 deletions.
2 changes: 0 additions & 2 deletions api/v1beta1/vmauth_types.go
Expand Up @@ -44,8 +44,6 @@ type VMAuthSpec struct {
// +kubebuilder:validation:Enum=default;json
LogFormat string `json:"logFormat,omitempty"`
// ReplicaCount is the expected size of the VMAuth
// it can be 0 or 1
// if you need more - use vm cluster
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Number of pods",xDescriptors="urn:alm:descriptor:com.tectonic.ui:podCount,urn:alm:descriptor:io.kubernetes:custom"
ReplicaCount *int32 `json:"replicaCount,omitempty"`

Expand Down
9 changes: 6 additions & 3 deletions api/v1beta1/vmuser_types.go
Expand Up @@ -49,10 +49,13 @@ type TargetRef struct {
// Paths - matched path to route.
// +optional
Paths []string `json:"paths,omitempty"`
// todo enable it if needed.
// QueryParams - additional query params for target.
// +optional

// QueryParams []string `json:"queryParams,omitempty"`
// TargetPathSuffix allows to add some suffix to the target path
// It allows to hide tenant configuration from user with crd as ref.
// it also may contain any url encoded params.
// +optional
TargetPathSuffix string `json:"target_path_suffx,omitempty"`
}

// CRDRef describe CRD target reference.
Expand Down
3 changes: 1 addition & 2 deletions config/crd/bases/operator.victoriametrics.com_vmauths.yaml
Expand Up @@ -756,8 +756,7 @@ spec:
type: integer
type: object
replicaCount:
description: ReplicaCount is the expected size of the VMAuth it can
be 0 or 1 if you need more - use vm cluster
description: ReplicaCount is the expected size of the VMAuth
format: int32
type: integer
resources:
Expand Down
6 changes: 6 additions & 0 deletions config/crd/bases/operator.victoriametrics.com_vmusers.yaml
Expand Up @@ -106,6 +106,12 @@ spec:
required:
- url
type: object
target_path_suffx:
description: QueryParams []string `json:"queryParams,omitempty"`
TargetPathSuffix allows to add some suffix to the target path
It allows to hide tenant configuration from user with crd as
ref. it also may contain any url encoded params.
type: string
type: object
type: array
username:
Expand Down
125 changes: 98 additions & 27 deletions controllers/factory/vmuser.go
Expand Up @@ -5,6 +5,8 @@ import (
"crypto/rand"
"fmt"
"math/big"
"net/url"
"path"
"sort"
"strings"

Expand Down Expand Up @@ -364,51 +366,120 @@ func generateVMAuthConfig(users []*v1beta1.VMUser, crdCache map[string]string) (
return yaml.Marshal(cfg)
}

// this function mutates user and fills missing fields,
// such password or username.
func genUserCfg(user *v1beta1.VMUser, crdUrlCache map[string]string) (yaml.MapSlice, error) {
r := yaml.MapSlice{}
func genUrlMaps(userName string, refs []v1beta1.TargetRef, result yaml.MapSlice, crdUrlCache map[string]string) (yaml.MapSlice, error) {
urlMaps := []yaml.MapSlice{}
for i := range user.Spec.TargetRefs {
// build url map.
urlMap := yaml.MapSlice{}
ref := user.Spec.TargetRefs[i]
if ref.Static == nil && ref.CRD == nil {
continue
}
handleRef := func(ref v1beta1.TargetRef) (string, error) {

var urlPrefix string
if ref.Static != nil {
if ref.Static.URL == "" {
return nil, fmt.Errorf("static.url cannot be empty for user: %s", user.Name)
return "", fmt.Errorf("static.url cannot be empty for user: %s", userName)
}
urlMap = append(urlMap, yaml.MapItem{
Key: "url_prefix",
Value: ref.Static.URL,
})
urlPrefix = ref.Static.URL

} else {
url := crdUrlCache[ref.CRD.AsKey()]
if url == "" {
return nil, fmt.Errorf("cannot find crdRef target: %q, for user: %s", ref.CRD.AsKey(), user.Name)
urlPrefix = crdUrlCache[ref.CRD.AsKey()]
if urlPrefix == "" {
return "", fmt.Errorf("cannot find crdRef target: %q, for user: %s", ref.CRD.AsKey(), userName)
}
urlMap = append(urlMap, yaml.MapItem{
Key: "url_prefix",
Value: crdUrlCache[ref.CRD.AsKey()],
})

}
if ref.TargetPathSuffix != "" {
parsedSuffix, err := url.Parse(ref.TargetPathSuffix)
if err != nil {
return "", fmt.Errorf("cannot parse targetPath: %q, err: %w", ref.TargetPathSuffix, err)
}

parsedUrlPrefix, err := url.Parse(urlPrefix)
if err != nil {
return "", fmt.Errorf("cannot parse urlPrefix: %q,err: %w", urlPrefix, err)
}
parsedUrlPrefix.Path = path.Join(parsedUrlPrefix.Path, parsedSuffix.Path)
suffixQuery := parsedSuffix.Query()
// update query params if needed.
if len(suffixQuery) > 0 {
urlQ := parsedUrlPrefix.Query()
for k, v := range suffixQuery {
urlQ[k] = v
}
parsedUrlPrefix.RawQuery = urlQ.Encode()
}

urlPrefix = parsedUrlPrefix.String()
}
return urlPrefix, nil
}
if len(refs) == 1 && len(refs[0].Paths) < 2 {
srcPaths := refs[0].Paths
var isDefaultRoute bool
switch len(srcPaths) {
case 0:
// default route to everything
isDefaultRoute = true
case 1:
// probably default route
switch srcPaths[0] {
case "/", "/*", "/.*":
isDefaultRoute = true
}

}
// special case, use different config syntax.
if isDefaultRoute {
ref := refs[0]
urlPrefix, err := handleRef(ref)
if err != nil {
return result, fmt.Errorf("cannot build urlPrefix for one ref, err: %w", err)
}
result = append(result, yaml.MapItem{Key: "url_prefix", Value: urlPrefix})
return result, nil
}

}

for i := range refs {
urlMap := yaml.MapSlice{}
ref := refs[i]
if ref.Static == nil && ref.CRD == nil {
continue
}
urlPrefix, err := handleRef(ref)
if err != nil {
return result, err
}

paths := ref.Paths
if len(paths) == 0 {
paths = append(paths, "/.*")
}
if len(paths) == 1 {
switch paths[0] {
case "/", "/*":
paths = []string{"/.*"}
}
}
urlMap = append(urlMap, yaml.MapItem{
Key: "url_prefix",
Value: urlPrefix,
})
urlMap = append(urlMap, yaml.MapItem{
Key: "src_paths",
Value: paths,
})
urlMaps = append(urlMaps, urlMap)
}
// no routes was found, nothing to do.
if len(urlMaps) == 0 {
return nil, fmt.Errorf("no routes was found for VMuser: %s, ns: %s", user.Name, user.Namespace)
result = append(result, yaml.MapItem{Key: "url_map", Value: urlMaps})
return result, nil
}

// this function mutates user and fills missing fields,
// such password or username.
func genUserCfg(user *v1beta1.VMUser, crdUrlCache map[string]string) (yaml.MapSlice, error) {
r := yaml.MapSlice{}
r, err := genUrlMaps(user.Name, user.Spec.TargetRefs, r, crdUrlCache)
if err != nil {
return nil, fmt.Errorf("cannot generate urlMaps for user: %w", err)
}
r = append(r, yaml.MapItem{Key: "url_map", Value: urlMaps})

// generate user access config.
var username, password, token string
Expand Down
108 changes: 88 additions & 20 deletions controllers/factory/vmuser_test.go
Expand Up @@ -99,6 +99,89 @@ password: pass
src_paths:
- /.*
bearer_token: secret-token
`,
},
{
name: "with crd and custom suffix",
args: args{
user: &v1beta1.VMUser{
Spec: v1beta1.VMUserSpec{
BearerToken: pointer.StringPtr("secret-token"),
TargetRefs: []v1beta1.TargetRef{
{
CRD: &v1beta1.CRDRef{
Kind: "VMAgent",
Name: "base",
Namespace: "monitoring",
},
TargetPathSuffix: "/insert/0/prometheus?extra_label=key=value",
Paths: []string{
"/api/v1/write",
"/api/v1/targets",
"/targets",
},
},
{
Static: &v1beta1.StaticRef{URL: "http://vmcluster-remote.mydomain.com:8401"},
TargetPathSuffix: "/insert/0/prometheus?extra_label=key=value",
Paths: []string{
"/",
},
},
{
CRD: &v1beta1.CRDRef{
Kind: "VMSingle",
Namespace: "monitoring",
Name: "db",
},
},
},
},
},
crdUrlCache: map[string]string{
"VMAgent/monitoring/base": "http://vmagent-base.monitoring.svc:8429",
"VMSingle/monitoring/db": "http://vmsingle-b.monitoring.svc:8429",
},
},
want: `url_map:
- url_prefix: http://vmagent-base.monitoring.svc:8429/insert/0/prometheus?extra_label=key%3Dvalue
src_paths:
- /api/v1/write
- /api/v1/targets
- /targets
- url_prefix: http://vmcluster-remote.mydomain.com:8401/insert/0/prometheus?extra_label=key%3Dvalue
src_paths:
- /.*
- url_prefix: http://vmsingle-b.monitoring.svc:8429
src_paths:
- /.*
bearer_token: secret-token
`,
},
{
name: "with one target",
args: args{
user: &v1beta1.VMUser{
Spec: v1beta1.VMUserSpec{
BearerToken: pointer.StringPtr("secret-token"),
TargetRefs: []v1beta1.TargetRef{
{
CRD: &v1beta1.CRDRef{
Kind: "VMAgent",
Name: "base",
Namespace: "monitoring",
},
},
},
},
},
crdUrlCache: map[string]string{
"VMAgent/monitoring/base": "http://vmagent-base.monitoring.svc:8429",
"VMSingle/monitoring/db": "http://vmsingle-b.monitoring.svc:8429",
},
},
want: `url_prefix: http://vmagent-base.monitoring.svc:8429
bearer_token: secret-token
`,
},
}
Expand Down Expand Up @@ -282,15 +365,9 @@ func Test_buildVMAuthConfig(t *testing.T) {
},
},
want: `users:
- url_map:
- url_prefix: http://some-static
src_paths:
- /
- url_prefix: http://some-static
bearer_token: bearer
- url_map:
- url_prefix: http://vmagent-test.default.svc:8429
src_paths:
- /
- url_prefix: http://vmagent-test.default.svc:8429
bearer_token: bearer-token-2
`,
},
Expand Down Expand Up @@ -380,20 +457,11 @@ func Test_buildVMAuthConfig(t *testing.T) {
},
},
want: `users:
- url_map:
- url_prefix: http://some-static
src_paths:
- /
- url_prefix: http://some-static
bearer_token: bearer
- url_map:
- url_prefix: http://vmagent-test.default.svc:8429
src_paths:
- /
- url_prefix: http://vmagent-test.default.svc:8429
bearer_token: bearer-token-2
- url_map:
- url_prefix: http://vmagent-test.default.svc:8429
src_paths:
- /
- url_prefix: http://vmagent-test.default.svc:8429
username: some-user
password: generated-password
`,
Expand Down

0 comments on commit 5dcd998

Please sign in to comment.