Skip to content
This repository was archived by the owner on Jun 11, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .tools/nvim/__http__/console/logs-and-metrics.rest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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'

---

Expand Down
30 changes: 28 additions & 2 deletions .tools/nvim/__http__/infra/vpn-devices.graphql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
global:
namespace: sample
name: s1
namespace: sample-nxtcoder17
clusterName: sample-cluster2
---

label: List VPN Devices
Expand Down Expand Up @@ -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
Expand Down
35 changes: 8 additions & 27 deletions apps/console/internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package app

import (
"context"
"fmt"
"net/http"
"strconv"
"time"

"github.com/kloudlite/api/pkg/errors"
Expand All @@ -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"
Expand Down Expand Up @@ -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{
Expand All @@ -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())
})
},
),
Expand Down
21 changes: 5 additions & 16 deletions apps/console/internal/app/observability-handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"time"

"github.com/kloudlite/api/pkg/errors"
fn "github.com/kloudlite/api/pkg/functions"
)

type ObservabilityArgs struct {
Expand Down Expand Up @@ -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)
Expand All @@ -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()

Expand All @@ -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 {
Expand Down
51 changes: 45 additions & 6 deletions apps/iam/internal/app/grpc-server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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, ","))
Expand All @@ -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)] {
Expand Down
3 changes: 3 additions & 0 deletions apps/iam/internal/entities/role-binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down
14 changes: 13 additions & 1 deletion apps/iam/types/types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package types

import "fmt"
import (
"fmt"
"strings"
)

type ResourceType string

Expand Down Expand Up @@ -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
}
2 changes: 0 additions & 2 deletions apps/infra/internal/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}

Expand Down