diff --git a/.tools/nvim/__http__/console/logs-and-metrics.rest.yml b/.tools/nvim/__http__/console/logs-and-metrics.rest.yml index d5aad41ab..96d75c0cc 100644 --- a/.tools/nvim/__http__/console/logs-and-metrics.rest.yml +++ b/.tools/nvim/__http__/console/logs-and-metrics.rest.yml @@ -7,8 +7,8 @@ global: clusterName: "nova-303453" clusterNamespace: "kl-account-nova" - endTime: '1700803914' - startTime: '1700717575' + endTime: '1707205625' + startTime: '1707119214' --- label: Get Logs for App @@ -27,8 +27,7 @@ query: label: Get Memory Metrics for App query: method: GET - # url: 'http://console-api.kl-core.svc.cluster.local:9100/observability/metrics/memory?workspace_name={{.workspaceName}}&start_time={{.startTime}}&end_time={{.endTime}}' - url: 'http://console-api.karthik-testing.svc.cluster.local:9100/observability/metrics/memory?cluster_name=sample-cluster&tracking_id=app-k-zmtg0km7epjj-fq89uvao14-3-l' + url: 'http://console-api.kloudlite.svc.cluster.local:9100/observability/metrics/memory?cluster_name=ab-cluster-3&tracking_id=app-3ez2fpr-3oc8gqjib-ii5-pbat6d&step=5m' --- diff --git a/.tools/nvim/__http__/infra/vpn-devices.graphql.yml b/.tools/nvim/__http__/infra/vpn-devices.graphql.yml index 2aa6701bc..099a5329f 100644 --- a/.tools/nvim/__http__/infra/vpn-devices.graphql.yml +++ b/.tools/nvim/__http__/infra/vpn-devices.graphql.yml @@ -2,8 +2,6 @@ global: namespace: sample name: s1 - namespace: sample-nxtcoder17 - clusterName: sample-cluster2 --- label: List VPN Devices @@ -51,6 +49,34 @@ query: |+ } } +--- +label: "Get VPN Device" +query: |+ + query Core_getVPNDevices($name: String!) { + core_getVPNDevice(name: $name) { + displayName + metadata { + name + } + clusterName + projectName + spec { + activeNamespace + disabled + ports { + port + targetPort + } + } + wireguardConfig { + encoding + value + } + } + } +variables: + name: "baby-589403" + --- label: Create VPN Device diff --git a/apps/console/internal/app/app.go b/apps/console/internal/app/app.go index c2f8f0d06..bf759be36 100644 --- a/apps/console/internal/app/app.go +++ b/apps/console/internal/app/app.go @@ -2,8 +2,8 @@ package app import ( "context" + "fmt" "net/http" - "strconv" "time" "github.com/kloudlite/api/pkg/errors" @@ -22,7 +22,6 @@ import ( "github.com/kloudlite/api/constants" "github.com/kloudlite/api/grpc-interfaces/kloudlite.io/rpc/iam" "github.com/kloudlite/api/grpc-interfaces/kloudlite.io/rpc/infra" - fn "github.com/kloudlite/api/pkg/functions" "github.com/kloudlite/api/pkg/grpc" httpServer "github.com/kloudlite/api/pkg/http-server" "github.com/kloudlite/api/pkg/kv" @@ -94,12 +93,12 @@ var Module = fx.Module("app", clusterName := c.Query("cluster_name") if clusterName == "" { - return errors.New("query param (cluster_name) must be provided") + return c.Status(http.StatusBadRequest).JSON(map[string]any{"error": "query param (cluster_name) must be provided"}) } trackingId := c.Query("tracking_id") if trackingId == "" { - return errors.New("query param (tracking_id) must be provided") + return c.Status(http.StatusBadRequest).JSON(map[string]any{"error": "query param (tracking_id) must be provided"}) } can, err := iamCli.Can(c.Context(), &iam.CanIn{ @@ -114,38 +113,20 @@ var Module = fx.Module("app", } if !can.Status { - return &fiber.Error{Code: http.StatusUnauthorized, Message: errors.NewEf(err, "unauthorized to view metrics for resources belonging to account (%s)", cc.AccountName).Error()} + return &fiber.Error{Code: http.StatusUnauthorized, Message: fmt.Sprintf("unauthorized to view metrics for resources belonging to account (%s)", cc.AccountName)} } metricType := c.Params("metric_type") - st := c.Query("start_time") - et := c.Query("end_time") - - var startTime *time.Time - var endTime *time.Time - - if st != "" { - st, err := strconv.ParseInt(st, 10, 64) - if err != nil { - return errors.NewE(err) - } - startTime = fn.New(time.Unix(st, 0)) - } - - if et != "" { - et, err := strconv.ParseInt(et, 10, 64) - if err != nil { - return errors.NewE(err) - } - endTime = fn.New(time.Unix(et, 0)) - } + st := c.Query("start_time", fmt.Sprintf("%d", time.Now().Add(-3*time.Hour).Unix())) + et := c.Query("end_time", fmt.Sprintf("%d", time.Now().Unix())) + step := c.Query("step", "5m") return queryProm(ev.PromHttpAddr, PromMetricsType(metricType), map[string]string{ "kl_account_name": cc.AccountName, "kl_cluster_name": clusterName, "kl_tracking_id": trackingId, - }, startTime, endTime, c.Response().BodyWriter()) + }, st, et, step, c.Response().BodyWriter()) }) }, ), diff --git a/apps/console/internal/app/observability-handler.go b/apps/console/internal/app/observability-handler.go index 46d82a48c..91881fd67 100644 --- a/apps/console/internal/app/observability-handler.go +++ b/apps/console/internal/app/observability-handler.go @@ -10,7 +10,6 @@ import ( "time" "github.com/kloudlite/api/pkg/errors" - fn "github.com/kloudlite/api/pkg/functions" ) type ObservabilityArgs struct { @@ -109,10 +108,9 @@ func buildPromQuery(resType PromMetricsType, filters map[string]string) (string, default: return "", errors.New("unknown prom metrics type provided") } - } -func queryProm(promAddr string, resType PromMetricsType, filters map[string]string, startTime *time.Time, endTime *time.Time, writer io.Writer) error { +func queryProm(promAddr string, resType PromMetricsType, filters map[string]string, startTime string, endTime string, step string, writer io.Writer) error { promQuery, err := buildPromQuery(resType, filters) if err != nil { return errors.NewE(err) @@ -128,18 +126,9 @@ func queryProm(promAddr string, resType PromMetricsType, filters map[string]stri qp := u.Query() qp.Add("query", promQuery) - t := time.Now() - if startTime == nil { - startTime = fn.New(t.Add(-2 * 24 * time.Hour)) - } - if endTime == nil { - endTime = &t - } - - qp.Add("start", fmt.Sprintf("%d", startTime.Unix())) - qp.Add("end", fmt.Sprintf("%d", endTime.Unix())) - // qp.Add("step", "700") // 15 minute - qp.Add("step", "345") // 15 minute + qp.Add("start", startTime) + qp.Add("end", endTime) + qp.Add("step", step) u.RawQuery = qp.Encode() @@ -148,7 +137,7 @@ func queryProm(promAddr string, resType PromMetricsType, filters map[string]stri return errors.NewE(err) } - // fmt.Printf("[DEBUG]: prometheus actual request: %s\n", req.URL.String()) + fmt.Printf("[DEBUG]: prometheus actual request: %s\n", req.URL.String()) resp, err := http.DefaultClient.Do(req) if err != nil { diff --git a/apps/iam/internal/app/grpc-server.go b/apps/iam/internal/app/grpc-server.go index 94173dd2c..b6b9f46a9 100644 --- a/apps/iam/internal/app/grpc-server.go +++ b/apps/iam/internal/app/grpc-server.go @@ -10,6 +10,7 @@ import ( t "github.com/kloudlite/api/apps/iam/types" "github.com/kloudlite/api/grpc-interfaces/kloudlite.io/rpc/iam" "github.com/kloudlite/api/pkg/errors" + fn "github.com/kloudlite/api/pkg/functions" "github.com/kloudlite/api/pkg/logging" "github.com/kloudlite/api/pkg/repos" ) @@ -42,7 +43,7 @@ func (s *GrpcService) UpdateMembership(ctx context.Context, in *iam.UpdateMember rb.Role = t.Role(in.Role) if _, err = s.rbRepo.UpdateById(ctx, rb.Id, rb); err != nil { - return nil, errors.NewE(err) + return nil, errors.NewE(err) } return &iam.UpdateMembershipOut{ @@ -111,14 +112,48 @@ func (s *GrpcService) ListMembershipsForResource(ctx context.Context, in *iam.Me func (s *GrpcService) Can(ctx context.Context, in *iam.CanIn) (*iam.CanOut, error) { if strings.HasPrefix(in.UserId, "sys-user") { - return &iam.CanOut{Status: true}, nil + return &iam.CanOut{Status: true}, nil + } + + rb, ok := s.roleBindingMap[t.Action(in.Action)] + if !ok { + return &iam.CanOut{Status: false}, nil + } + + var hasAccountMemberRole bool + + canFilter := repos.Filter{ + "resource_ref": map[string]any{"$in": in.ResourceRefs}, + "user_id": in.UserId, + } + + for i := range rb { + if rb[i] == t.RoleAccountMember { + hasAccountMemberRole = true + + rr := make([]map[string]any, 0, len(in.ResourceRefs)) + + for i := range in.ResourceRefs { + accountName, _, _, err := t.ParseResourceRef(in.ResourceRefs[i]) + if err != nil { + return nil, err + } + nf := s.rbRepo.MergeMatchFilters(repos.Filter{}, map[string]repos.MatchFilter{ + "resource_ref": { + MatchType: repos.MatchTypeRegex, + Regex: fn.New(t.NewResourceRef(accountName, "*", "*")), + }, + }) + rr = append(rr, map[string]any{"resource_ref": nf["resource_ref"]}) + } + + delete(canFilter, "resource_ref") + canFilter["$or"] = rr + } } rbs, err := s.rbRepo.Find( - ctx, repos.Query{Filter: repos.Filter{ - "resource_ref": map[string]any{"$in": in.ResourceRefs}, - "user_id": in.UserId, - }}, + ctx, repos.Query{Filter: canFilter}, ) if err != nil { return nil, errors.NewEf(err, "could not find rolebindings for (resourceRefs=%s)", strings.Join(in.ResourceRefs, ",")) @@ -128,6 +163,10 @@ func (s *GrpcService) Can(ctx context.Context, in *iam.CanIn) (*iam.CanOut, erro return nil, errors.Newf("no rolebinding found for (userId=%s, resourceRefs=%s)", in.UserId, strings.Join(in.ResourceRefs, ",")) } + if hasAccountMemberRole && len(rbs) > 0 { + return &iam.CanOut{Status: true}, nil + } + for i := range rbs { // 2nd loop, but very small length (always < #roles), so it's not exactly O(n^2), much like XO(n) for _, role := range s.roleBindingMap[t.Action(in.Action)] { diff --git a/apps/iam/internal/entities/role-binding.go b/apps/iam/internal/entities/role-binding.go index 81dfbfa68..2b2e11c4d 100644 --- a/apps/iam/internal/entities/role-binding.go +++ b/apps/iam/internal/entities/role-binding.go @@ -20,12 +20,15 @@ func (rb *RoleBinding) Validate() error { if rb.UserId == "" { verr.Errors = append(verr.Errors, "user_id is required") } + if rb.ResourceType == "" { verr.Errors = append(verr.Errors, "resource_type is required") } + if rb.ResourceRef == "" { verr.Errors = append(verr.Errors, "resource_ref is required") } + if rb.Role == "" { verr.Errors = append(verr.Errors, "role is required") } diff --git a/apps/iam/types/types.go b/apps/iam/types/types.go index 1a15a4cea..7bde03e4f 100644 --- a/apps/iam/types/types.go +++ b/apps/iam/types/types.go @@ -1,6 +1,9 @@ package types -import "fmt" +import ( + "fmt" + "strings" +) type ResourceType string @@ -148,3 +151,12 @@ const ( func NewResourceRef(accountName string, resourceType ResourceType, resourceName string) string { return fmt.Sprintf("%s/%s/%s", accountName, resourceType, resourceName) } + +func ParseResourceRef(rref string) (accountName, resourceType, resourceName string, err error) { + sp := strings.SplitN(rref, "/", 3) + if len(sp) != 3 { + return "", "", "", fmt.Errorf("invalid resource ref %s", rref) + } + + return sp[0], sp[1], sp[2], nil +} diff --git a/apps/infra/internal/env/env.go b/apps/infra/internal/env/env.go index 3256b1c75..75e8f703b 100644 --- a/apps/infra/internal/env/env.go +++ b/apps/infra/internal/env/env.go @@ -43,8 +43,6 @@ type Env struct { MsvcTemplateFilePath string `env:"MSVC_TEMPLATE_FILE_PATH" required:"true"` - DeviceNamespace string `env:"DEVICE_NAMESPACE" required:"true"` - KloudliteRelease string `env:"KLOUDLITE_RELEASE" required:"true"` }