diff --git a/_run/common-commands.mk b/_run/common-commands.mk index e56efcc48..6beb0c4aa 100644 --- a/_run/common-commands.mk +++ b/_run/common-commands.mk @@ -94,7 +94,7 @@ get-manifest: .PHONY: deployment-create deployment-create: - $(AKASH) tx deployment create "$(SDL_PATH)" \ + $(PROVIDER_SERVICES) tx deployment create "$(SDL_PATH)" \ --dseq "$(DSEQ)" \ --from "$(KEY_NAME)" diff --git a/cluster/kube/apply.go b/cluster/kube/apply.go index 6c588a37c..e05b466ae 100644 --- a/cluster/kube/apply.go +++ b/cluster/kube/apply.go @@ -10,6 +10,7 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" netv1 "k8s.io/api/networking/v1" + rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8stypes "k8s.io/apimachinery/pkg/types" @@ -265,3 +266,82 @@ func applyService(ctx context.Context, kc kubernetes.Interface, b builder.Servic return nobj, uobj, oobj, err } + +func applyServiceAccount(ctx context.Context, kc kubernetes.Interface, b builder.ServiceAccount) (*corev1.ServiceAccount, *corev1.ServiceAccount, *corev1.ServiceAccount, error) { + oobj, err := kc.CoreV1().ServiceAccounts(b.NS()).Get(ctx, b.Name(), metav1.GetOptions{}) + metricsutils.IncCounterVecWithLabelValuesFiltered(kubeCallsCounter, "serviceaccounts-get", err, errors.IsNotFound) + + var nobj *corev1.ServiceAccount + var uobj *corev1.ServiceAccount + + switch { + case err == nil: + uobj, err = b.Update(oobj) + if err == nil && (!b.IsObjectRevisionLatest(uobj.Labels) || + !reflect.DeepEqual(uobj.Labels, oobj.Labels)) { + uobj, err = kc.CoreV1().ServiceAccounts(b.NS()).Update(ctx, uobj, metav1.UpdateOptions{}) + metricsutils.IncCounterVecWithLabelValues(kubeCallsCounter, "serviceaccounts-update", err) + } + case errors.IsNotFound(err): + nobj, err = b.Create() + if err == nil { + nobj, err = kc.CoreV1().ServiceAccounts(b.NS()).Create(ctx, nobj, metav1.CreateOptions{}) + metricsutils.IncCounterVecWithLabelValues(kubeCallsCounter, "serviceaccounts-create", err) + } + } + + return nobj, uobj, oobj, err +} + +func applyRole(ctx context.Context, kc kubernetes.Interface, b builder.Role) (*rbacv1.Role, *rbacv1.Role, *rbacv1.Role, error) { + oobj, err := kc.RbacV1().Roles(b.NS()).Get(ctx, b.Name(), metav1.GetOptions{}) + metricsutils.IncCounterVecWithLabelValuesFiltered(kubeCallsCounter, "roles-get", err, errors.IsNotFound) + + var nobj *rbacv1.Role + var uobj *rbacv1.Role + + switch { + case err == nil: + uobj, err = b.Update(oobj) + if err == nil && (!b.IsObjectRevisionLatest(uobj.Labels) || + !reflect.DeepEqual(&uobj.Rules, &oobj.Rules) || + !reflect.DeepEqual(uobj.Labels, oobj.Labels)) { + uobj, err = kc.RbacV1().Roles(b.NS()).Update(ctx, uobj, metav1.UpdateOptions{}) + metricsutils.IncCounterVecWithLabelValues(kubeCallsCounter, "roles-update", err) + } + case errors.IsNotFound(err): + nobj, err = b.Create() + if err == nil { + nobj, err = kc.RbacV1().Roles(b.NS()).Create(ctx, nobj, metav1.CreateOptions{}) + metricsutils.IncCounterVecWithLabelValues(kubeCallsCounter, "roles-create", err) + } + } + + return nobj, uobj, oobj, err +} + +func applyRoleBinding(ctx context.Context, kc kubernetes.Interface, b builder.RoleBinding) (*rbacv1.RoleBinding, *rbacv1.RoleBinding, *rbacv1.RoleBinding, error) { + oobj, err := kc.RbacV1().RoleBindings(b.NS()).Get(ctx, b.Name(), metav1.GetOptions{}) + metricsutils.IncCounterVecWithLabelValuesFiltered(kubeCallsCounter, "rolebindings-get", err, errors.IsNotFound) + + var nobj *rbacv1.RoleBinding + var uobj *rbacv1.RoleBinding + + switch { + case err == nil: + uobj, err = b.Update(oobj) + if err == nil && (!b.IsObjectRevisionLatest(uobj.Labels) || + !reflect.DeepEqual(uobj.Labels, oobj.Labels)) { + uobj, err = kc.RbacV1().RoleBindings(b.NS()).Update(ctx, uobj, metav1.UpdateOptions{}) + metricsutils.IncCounterVecWithLabelValues(kubeCallsCounter, "rolebindings-update", err) + } + case errors.IsNotFound(err): + nobj, err = b.Create() + if err == nil { + nobj, err = kc.RbacV1().RoleBindings(b.NS()).Create(ctx, nobj, metav1.CreateOptions{}) + metricsutils.IncCounterVecWithLabelValues(kubeCallsCounter, "rolebindings-create", err) + } + } + + return nobj, uobj, oobj, err +} diff --git a/cluster/kube/builder/builder.go b/cluster/kube/builder/builder.go index 7a22d1539..4dc004e06 100644 --- a/cluster/kube/builder/builder.go +++ b/cluster/kube/builder/builder.go @@ -51,12 +51,15 @@ const ( ) const ( - envVarAkashGroupSequence = "AKASH_GROUP_SEQUENCE" - envVarAkashDeploymentSequence = "AKASH_DEPLOYMENT_SEQUENCE" - envVarAkashOrderSequence = "AKASH_ORDER_SEQUENCE" - envVarAkashOwner = "AKASH_OWNER" - envVarAkashProvider = "AKASH_PROVIDER" - envVarAkashClusterPublicHostname = "AKASH_CLUSTER_PUBLIC_HOSTNAME" + envVarAkashGroupSequence = "AKASH_GROUP_SEQUENCE" + envVarAkashDeploymentSequence = "AKASH_DEPLOYMENT_SEQUENCE" + envVarAkashOrderSequence = "AKASH_ORDER_SEQUENCE" + envVarAkashOwner = "AKASH_OWNER" + envVarAkashProvider = "AKASH_PROVIDER" + envVarAkashClusterPublicHostname = "AKASH_CLUSTER_PUBLIC_HOSTNAME" + envVarKubernetesNamespaceOverride = "KUBERNETES_NAMESPACE_OVERRIDE" + envVarKubernetesServiceHost = "KUBERNETES_SERVICE_HOST" + envVarKubernetesServicePort = "KUBERNETES_SERVICE_PORT" ) var ( diff --git a/cluster/kube/builder/deployment.go b/cluster/kube/builder/deployment.go index beacdb47f..f20fd4c81 100644 --- a/cluster/kube/builder/deployment.go +++ b/cluster/kube/builder/deployment.go @@ -65,7 +65,8 @@ func (b *deployment) Create() (*appsv1.Deployment, error) { // nolint:unparam SecurityContext: &corev1.PodSecurityContext{ RunAsNonRoot: &falseValue, }, - AutomountServiceAccountToken: &falseValue, + AutomountServiceAccountToken: b.automountServiceAccountToken(), + ServiceAccountName: b.serviceAccountName(), Containers: []corev1.Container{b.container()}, ImagePullSecrets: b.secretsRefs, Volumes: b.volumesObjs, @@ -86,6 +87,8 @@ func (b *deployment) Update(obj *appsv1.Deployment) (*appsv1.Deployment, error) uobj.Spec.Template.Labels = b.labels() uobj.Spec.Template.Spec.Affinity = b.affinity() uobj.Spec.Template.Spec.RuntimeClassName = b.runtimeClass() + uobj.Spec.Template.Spec.AutomountServiceAccountToken = b.automountServiceAccountToken() + uobj.Spec.Template.Spec.ServiceAccountName = b.serviceAccountName() uobj.Spec.Template.Spec.Containers = []corev1.Container{b.container()} uobj.Spec.Template.Spec.ImagePullSecrets = b.secretsRefs uobj.Spec.Template.Spec.Volumes = b.volumesObjs diff --git a/cluster/kube/builder/deployment_test.go b/cluster/kube/builder/deployment_test.go index fc1f72e90..e3b92e4c6 100644 --- a/cluster/kube/builder/deployment_test.go +++ b/cluster/kube/builder/deployment_test.go @@ -10,19 +10,23 @@ import ( "pkg.akt.dev/go/testutil" crd "github.com/akash-network/provider/pkg/apis/akash.network/v2beta2" + mtypes "pkg.akt.dev/go/node/market/v1" ) -func TestDeploySetsEnvironmentVariables(t *testing.T) { +const fakeHostname = "ahostname.dev" + +func testSetup(t *testing.T, sdlFile string, serviceIdx int, lid mtypes.LeaseID) (*crd.Manifest, *Workload) { + t.Helper() + log := testutil.Logger(t) - const fakeHostname = "ahostname.dev" settings := Settings{ ClusterPublicHostname: fakeHostname, } - lid := testutil.LeaseID(t) - sdl, err := sdl.ReadFile("../../../testdata/deployment/deployment.yaml") + + sdlData, err := sdl.ReadFile(sdlFile) require.NoError(t, err) - mani, err := sdl.Manifest() + mani, err := sdlData.Manifest() require.NoError(t, err) sparams := make([]*crd.SchedulerParams, len(mani.GetGroups()[0].Services)) @@ -39,9 +43,16 @@ func TestDeploySetsEnvironmentVariables(t *testing.T) { Sparams: crd.ClusterSettings{SchedulerParams: sparams}, } - workload, err := NewWorkloadBuilder(log, settings, cdep, cmani, 0) + workload, err := NewWorkloadBuilder(log, settings, cdep, cmani, serviceIdx) require.NoError(t, err) + return cmani, workload +} + +func TestDeploySetsEnvironmentVariables(t *testing.T) { + lid := testutil.LeaseID(t) + _, workload := testSetup(t, "../../../testdata/deployment/deployment.yaml", 0, lid) + deploymentBuilder := NewDeployment(workload) require.NotNil(t, deploymentBuilder) @@ -80,3 +91,46 @@ func TestDeploySetsEnvironmentVariables(t *testing.T) { require.True(t, ok) require.Equal(t, lid.Provider, value) } + +func TestDeploymentPermissions(t *testing.T) { + tests := []struct { + name string + sdlFile string + serviceIdx int + expectedToken bool + }{ + { + name: "defaults to false when no permissions specified", + sdlFile: "../../../testdata/deployment/deployment.yaml", + serviceIdx: 0, + expectedToken: false, + }, + { + name: "enables automount when permissions are defined", + sdlFile: "../../../testdata/sdl/permissions.yaml", + serviceIdx: 0, + expectedToken: true, + }, + { + name: "disables automount when no permissions defined", + sdlFile: "../../../testdata/sdl/permissions.yaml", + serviceIdx: 1, + expectedToken: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + lid := testutil.LeaseID(t) + _, workload := testSetup(t, tt.sdlFile, tt.serviceIdx, lid) + + deploymentBuilder := NewDeployment(workload) + deployment, err := deploymentBuilder.Create() + require.NoError(t, err) + require.NotNil(t, deployment) + + require.NotNil(t, deployment.Spec.Template.Spec.AutomountServiceAccountToken) + require.Equal(t, tt.expectedToken, *deployment.Spec.Template.Spec.AutomountServiceAccountToken) + }) + } +} diff --git a/cluster/kube/builder/permissions.go b/cluster/kube/builder/permissions.go new file mode 100644 index 000000000..860c99529 --- /dev/null +++ b/cluster/kube/builder/permissions.go @@ -0,0 +1,180 @@ +package builder + +import ( + rbacv1 "k8s.io/api/rbac/v1" +) + +// PermissionType represents the type of permission action (read, write, delete) +type PermissionType string + +const ( + PermissionTypeRead PermissionType = "read" +) + +// WorkloadPermissions holds permissions organized by type for a workload. +// This structure supports Read permissions. +type WorkloadPermissions struct { + Read []string +} + +// HasAny returns true if any permissions are defined +func (p *WorkloadPermissions) HasAny() bool { + if p == nil { + return false + } + return len(p.Read) > 0 +} + +// HasRead returns true if any read permissions are defined +func (p *WorkloadPermissions) HasRead() bool { + if p == nil { + return false + } + return len(p.Read) > 0 +} + +// GetByType returns the permissions for a given type +func (p *WorkloadPermissions) GetByType(permType PermissionType) []string { + if p == nil { + return nil + } + switch permType { + case PermissionTypeRead: + return p.Read + default: + return nil + } +} + +// PolicyRuleGenerator generates Kubernetes RBAC policy rules for a given resource +type PolicyRuleGenerator func(resource string, permType PermissionType) []rbacv1.PolicyRule + +// ResourcePolicyMapping defines the policy rules for a specific resource type. +// This allows easy extension when new resources need to be supported. +type ResourcePolicyMapping struct { + // Name is the resource identifier (e.g., "logs", "events") + Name string + // Generator creates the policy rules for this resource based on permission type + Generator PolicyRuleGenerator +} + +// PolicyRuleRegistry holds all registered resource policy mappings +type PolicyRuleRegistry struct { + mappings map[string]ResourcePolicyMapping +} + +// NewPolicyRuleRegistry creates a new registry with default resource mappings +func NewPolicyRuleRegistry() *PolicyRuleRegistry { + registry := &PolicyRuleRegistry{ + mappings: make(map[string]ResourcePolicyMapping), + } + + // Register default resource handlers + registry.Register(logsResourceMapping()) + // TODO: enable events in a different PR and test them separately. + // registry.Register(eventsResourceMapping()) + + return registry +} + +// Register adds a new resource policy mapping to the registry +func (r *PolicyRuleRegistry) Register(mapping ResourcePolicyMapping) { + r.mappings[mapping.Name] = mapping +} + +// GenerateRules generates all policy rules for the given permissions +func (r *PolicyRuleRegistry) GenerateRules(permissions *WorkloadPermissions) []rbacv1.PolicyRule { + if permissions == nil { + return nil + } + + var rules []rbacv1.PolicyRule + seen := make(map[string]map[PermissionType]bool) + + // Process each permission type + for _, permConfig := range []struct { + permType PermissionType + resources []string + }{ + {PermissionTypeRead, permissions.Read}, + } { + for _, resource := range permConfig.resources { + // Initialize the inner map if needed + if seen[resource] == nil { + seen[resource] = make(map[PermissionType]bool) + } + + // Skip if we've already processed this resource+permType combination + if seen[resource][permConfig.permType] { + continue + } + seen[resource][permConfig.permType] = true + + // Look up the mapping and generate rules + if mapping, ok := r.mappings[resource]; ok { + rules = append(rules, mapping.Generator(resource, permConfig.permType)...) + } + } + } + + return rules +} + +// logsResourceMapping returns the policy mapping for "logs" resource +func logsResourceMapping() ResourcePolicyMapping { + return ResourcePolicyMapping{ + Name: "logs", + Generator: func(resource string, permType PermissionType) []rbacv1.PolicyRule { + switch permType { + case PermissionTypeRead: + return []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"pods"}, + Verbs: []string{"get", "list", "watch"}, + }, + { + APIGroups: []string{""}, + Resources: []string{"pods/log"}, + Verbs: []string{"get"}, + }, + { + APIGroups: []string{"apps"}, + Resources: []string{"deployments"}, + Verbs: []string{"get", "list", "watch"}, + }, + } + default: + return nil + } + }, + } +} + +// TODO: enable events in a different PR and test them separately. +// eventsResourceMapping returns the policy mapping for "events" resource +// func eventsResourceMapping() ResourcePolicyMapping { +// return ResourcePolicyMapping{ +// Name: "events", +// Generator: func(resource string, permType PermissionType) []rbacv1.PolicyRule { +// switch permType { +// case PermissionTypeRead: +// return []rbacv1.PolicyRule{ +// { +// APIGroups: []string{""}, +// Resources: []string{"events"}, +// Verbs: []string{"get", "list", "watch"}, +// }, +// } +// case PermissionTypeWrite: +// // Future: Add write rules for events when implemented +// return nil +// case PermissionTypeDelete: +// // Future: Add delete rules for events when implemented +// return nil +// default: +// return nil +// } +// }, +// } +// } diff --git a/cluster/kube/builder/permissions_test.go b/cluster/kube/builder/permissions_test.go new file mode 100644 index 000000000..ea67ed1bb --- /dev/null +++ b/cluster/kube/builder/permissions_test.go @@ -0,0 +1,175 @@ +package builder + +import ( + "testing" + + "github.com/stretchr/testify/require" + rbacv1 "k8s.io/api/rbac/v1" +) + +func TestWorkloadPermissions_HasAny(t *testing.T) { + tests := []struct { + name string + perms *WorkloadPermissions + expected bool + }{ + { + name: "nil permissions returns false", + perms: nil, + expected: false, + }, + { + name: "empty permissions returns false", + perms: &WorkloadPermissions{}, + expected: false, + }, + { + name: "only read permissions returns true", + perms: &WorkloadPermissions{ + Read: []string{"logs"}, + }, + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := tt.perms.HasAny() + require.Equal(t, tt.expected, result) + }) + } +} + +func TestWorkloadPermissions_HasRead(t *testing.T) { + tests := []struct { + name string + perms *WorkloadPermissions + expected bool + }{ + { + name: "nil permissions returns false", + perms: nil, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := tt.perms.HasRead() + require.Equal(t, tt.expected, result) + }) + } +} + +func TestWorkloadPermissions_GetByType(t *testing.T) { + perms := &WorkloadPermissions{ + Read: []string{"logs", "events"}, + } + + tests := []struct { + name string + permType PermissionType + expected []string + }{ + { + name: "get read permissions", + permType: PermissionTypeRead, + expected: []string{"logs", "events"}, + }, + { + name: "unknown type returns nil", + permType: PermissionType("unknown"), + expected: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := perms.GetByType(tt.permType) + require.Equal(t, tt.expected, result) + }) + } +} + +func TestPolicyRuleRegistry_GenerateRules(t *testing.T) { + registry := NewPolicyRuleRegistry() + + t.Run("nil permissions returns nil", func(t *testing.T) { + rules := registry.GenerateRules(nil) + require.Nil(t, rules) + }) + + t.Run("empty permissions returns nil", func(t *testing.T) { + rules := registry.GenerateRules(&WorkloadPermissions{}) + require.Nil(t, rules) + }) + + t.Run("logs read permission generates correct rules", func(t *testing.T) { + perms := &WorkloadPermissions{ + Read: []string{"logs"}, + } + rules := registry.GenerateRules(perms) + + require.Len(t, rules, 3) + + require.Contains(t, rules[0].Resources, "pods") + require.Contains(t, rules[0].Verbs, "get") + require.Contains(t, rules[0].Verbs, "list") + require.Contains(t, rules[0].Verbs, "watch") + + require.Contains(t, rules[1].Resources, "pods/log") + require.Contains(t, rules[1].Verbs, "get") + + require.Contains(t, rules[2].Resources, "deployments") + require.Equal(t, []string{"apps"}, rules[2].APIGroups) + }) + + t.Run("duplicate resources are deduplicated", func(t *testing.T) { + perms := &WorkloadPermissions{ + Read: []string{"logs", "logs", "logs"}, + } + rules := registry.GenerateRules(perms) + + // Should only generate rules once for logs + require.Len(t, rules, 3) + }) + + t.Run("unknown resource generates no rules", func(t *testing.T) { + perms := &WorkloadPermissions{ + Read: []string{"unknown-resource"}, + } + rules := registry.GenerateRules(perms) + + require.Len(t, rules, 0) + }) +} + +func TestPolicyRuleRegistry_Register(t *testing.T) { + registry := NewPolicyRuleRegistry() + + // Register a custom resource mapping + registry.Register(ResourcePolicyMapping{ + Name: "custom-resource", + Generator: func(resource string, permType PermissionType) []rbacv1.PolicyRule { + if permType == PermissionTypeRead { + return []rbacv1.PolicyRule{ + { + APIGroups: []string{"custom.io"}, + Resources: []string{"customs"}, + Verbs: []string{"get"}, + }, + } + } + return nil + }, + }) + + perms := &WorkloadPermissions{ + Read: []string{"custom-resource"}, + } + rules := registry.GenerateRules(perms) + + require.Len(t, rules, 1) + require.Equal(t, []string{"custom.io"}, rules[0].APIGroups) + require.Contains(t, rules[0].Resources, "customs") +} diff --git a/cluster/kube/builder/role.go b/cluster/kube/builder/role.go new file mode 100644 index 000000000..7880264e4 --- /dev/null +++ b/cluster/kube/builder/role.go @@ -0,0 +1,75 @@ +package builder + +import ( + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type Role interface { + builderBase + Create() (*rbacv1.Role, error) + Update(obj *rbacv1.Role) (*rbacv1.Role, error) +} + +type role struct { + *Workload + registry *PolicyRuleRegistry +} + +var _ Role = (*role)(nil) + +// BuildRole creates a new Role builder for the given workload. +// It uses the default PolicyRuleRegistry which includes mappings for +// all supported resource types (logs, events, etc.). +func BuildRole(workload *Workload) Role { + return &role{ + Workload: workload, + registry: NewPolicyRuleRegistry(), + } +} + +// BuildRoleWithRegistry creates a new Role builder with a custom PolicyRuleRegistry. +// This is useful for testing or for cases where custom resource mappings are needed. +func BuildRoleWithRegistry(workload *Workload, registry *PolicyRuleRegistry) Role { + return &role{ + Workload: workload, + registry: registry, + } +} + +func (b *role) labels() map[string]string { + return AppendLeaseLabels(b.deployment.LeaseID(), b.Workload.labels()) +} + +// buildPolicyRules generates Kubernetes RBAC policy rules based on the +// workload permissions. It uses the PolicyRuleRegistry to map resource +// types (logs, events, etc.) to their corresponding RBAC rules. +// +// The registry pattern allows easy extension when new permission types +// (write, delete) or new resources are added in the future. +func (b *role) buildPolicyRules() []rbacv1.PolicyRule { + permissions := b.GetWorkloadPermissions() + if permissions == nil || !permissions.HasAny() { + return nil + } + + return b.registry.GenerateRules(permissions) +} + +func (b *role) Create() (*rbacv1.Role, error) { + return &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: b.Name(), + Labels: b.labels(), + }, + Rules: b.buildPolicyRules(), + }, nil +} + +func (b *role) Update(obj *rbacv1.Role) (*rbacv1.Role, error) { + uobj := obj.DeepCopy() + uobj.Name = b.Name() + uobj.Labels = updateAkashLabels(obj.Labels, b.labels()) + uobj.Rules = b.buildPolicyRules() + return uobj, nil +} diff --git a/cluster/kube/builder/role_test.go b/cluster/kube/builder/role_test.go new file mode 100644 index 000000000..51bc7d0fd --- /dev/null +++ b/cluster/kube/builder/role_test.go @@ -0,0 +1,226 @@ +package builder + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "pkg.akt.dev/go/sdl" + "pkg.akt.dev/go/testutil" + + crd "github.com/akash-network/provider/pkg/apis/akash.network/v2beta2" +) + +func TestRolePolicyRules(t *testing.T) { + log := testutil.Logger(t) + const fakeHostname = "ahostname.dev" + settings := Settings{ + ClusterPublicHostname: fakeHostname, + } + lid := testutil.LeaseID(t) + + t.Run("generates logs permissions correctly", func(t *testing.T) { + sdl, err := sdl.ReadFile("../../../testdata/sdl/permissions.yaml") + require.NoError(t, err) + + mani, err := sdl.Manifest() + require.NoError(t, err) + + sparams := make([]*crd.SchedulerParams, len(mani.GetGroups()[0].Services)) + + cmani, err := crd.NewManifest("lease", lid, &mani.GetGroups()[0], crd.ClusterSettings{SchedulerParams: sparams}) + require.NoError(t, err) + + group, sparams, err := cmani.Spec.Group.FromCRD() + require.NoError(t, err) + + cdep := &ClusterDeployment{ + Lid: lid, + Group: &group, + Sparams: crd.ClusterSettings{SchedulerParams: sparams}, + } + + // Test service with permissions.read: [logs] (index 0 = web) + workload, err := NewWorkloadBuilder(log, settings, cdep, cmani, 0) + require.NoError(t, err) + + roleBuilder := BuildRole(workload) + role, err := roleBuilder.Create() + require.NoError(t, err) + require.NotNil(t, role) + + // Should have 3 rules for logs: pods, pods/log, deployments + require.Len(t, role.Rules, 3) + + // Check pods rule + require.Contains(t, role.Rules[0].Resources, "pods") + require.Contains(t, role.Rules[0].Verbs, "get") + require.Contains(t, role.Rules[0].Verbs, "list") + require.Contains(t, role.Rules[0].Verbs, "watch") + + // Check pods/log rule + require.Contains(t, role.Rules[1].Resources, "pods/log") + require.Contains(t, role.Rules[1].Verbs, "get") + + // Check deployments rule + require.Contains(t, role.Rules[2].Resources, "deployments") + require.Equal(t, []string{"apps"}, role.Rules[2].APIGroups) + require.Contains(t, role.Rules[2].Verbs, "get") + require.Contains(t, role.Rules[2].Verbs, "list") + require.Contains(t, role.Rules[2].Verbs, "watch") + }) + + // TODO: enable events test in a different PR and test them separately. + // t.Run("generates logs and events permissions correctly", func(t *testing.T) { + // sdl, err := sdl.ReadFile("../../../testdata/sdl/permissions-events.yaml") + // require.NoError(t, err) + // + // mani, err := sdl.Manifest() + // require.NoError(t, err) + // + // sparams := make([]*crd.SchedulerParams, len(mani.GetGroups()[0].Services)) + // + // cmani, err := crd.NewManifest("lease", lid, &mani.GetGroups()[0], crd.ClusterSettings{SchedulerParams: sparams}) + // require.NoError(t, err) + // + // group, sparams, err := cmani.Spec.Group.FromCRD() + // require.NoError(t, err) + // + // cdep := &ClusterDeployment{ + // Lid: lid, + // Group: &group, + // Sparams: crd.ClusterSettings{SchedulerParams: sparams}, + // } + // + // // Test service with permissions.read: [logs, events] (index 0 = log-collector) + // workload, err := NewWorkloadBuilder(log, settings, cdep, cmani, 0) + // require.NoError(t, err) + // + // roleBuilder := BuildRole(workload) + // role, err := roleBuilder.Create() + // require.NoError(t, err) + // require.NotNil(t, role) + // + // // Should have 4 rules: pods, pods/log, deployments (from logs), events (from events) + // require.Len(t, role.Rules, 4) + // + // // Check events rule (should be last) + // require.Contains(t, role.Rules[3].Resources, "events") + // require.Contains(t, role.Rules[3].Verbs, "get") + // require.Contains(t, role.Rules[3].Verbs, "list") + // require.Contains(t, role.Rules[3].Verbs, "watch") + // }) + + t.Run("generates empty rules when no permissions", func(t *testing.T) { + sdl, err := sdl.ReadFile("../../../testdata/sdl/permissions.yaml") + require.NoError(t, err) + + mani, err := sdl.Manifest() + require.NoError(t, err) + + sparams := make([]*crd.SchedulerParams, len(mani.GetGroups()[0].Services)) + + cmani, err := crd.NewManifest("lease", lid, &mani.GetGroups()[0], crd.ClusterSettings{SchedulerParams: sparams}) + require.NoError(t, err) + + group, sparams, err := cmani.Spec.Group.FromCRD() + require.NoError(t, err) + + cdep := &ClusterDeployment{ + Lid: lid, + Group: &group, + Sparams: crd.ClusterSettings{SchedulerParams: sparams}, + } + + // Test service without permissions (index 1 = web2) + workload, err := NewWorkloadBuilder(log, settings, cdep, cmani, 1) + require.NoError(t, err) + + roleBuilder := BuildRole(workload) + role, err := roleBuilder.Create() + require.NoError(t, err) + require.NotNil(t, role) + + // Should have no rules when no permissions are defined + require.Len(t, role.Rules, 0) + }) +} + +func TestRBACResourceLabels(t *testing.T) { + log := testutil.Logger(t) + const fakeHostname = "ahostname.dev" + settings := Settings{ + ClusterPublicHostname: fakeHostname, + } + lid := testutil.LeaseID(t) + + sdl, err := sdl.ReadFile("../../../testdata/sdl/permissions.yaml") + require.NoError(t, err) + + mani, err := sdl.Manifest() + require.NoError(t, err) + + sparams := make([]*crd.SchedulerParams, len(mani.GetGroups()[0].Services)) + + cmani, err := crd.NewManifest("lease", lid, &mani.GetGroups()[0], crd.ClusterSettings{SchedulerParams: sparams}) + require.NoError(t, err) + + group, sparams, err := cmani.Spec.Group.FromCRD() + require.NoError(t, err) + + cdep := &ClusterDeployment{ + Lid: lid, + Group: &group, + Sparams: crd.ClusterSettings{SchedulerParams: sparams}, + } + + workload, err := NewWorkloadBuilder(log, settings, cdep, cmani, 0) + require.NoError(t, err) + + serviceName := group.Services[0].Name + + t.Run("ServiceAccount has manifest-service label", func(t *testing.T) { + saBuilder := BuildServiceAccount(workload) + sa, err := saBuilder.Create() + require.NoError(t, err) + require.NotNil(t, sa) + + // Verify AkashManifestServiceLabelName is present + require.Contains(t, sa.Labels, AkashManifestServiceLabelName) + require.Equal(t, serviceName, sa.Labels[AkashManifestServiceLabelName]) + + // Verify AkashManagedLabelName is present + require.Contains(t, sa.Labels, AkashManagedLabelName) + require.Equal(t, "true", sa.Labels[AkashManagedLabelName]) + }) + + t.Run("Role has manifest-service label", func(t *testing.T) { + roleBuilder := BuildRole(workload) + role, err := roleBuilder.Create() + require.NoError(t, err) + require.NotNil(t, role) + + // Verify AkashManifestServiceLabelName is present + require.Contains(t, role.Labels, AkashManifestServiceLabelName) + require.Equal(t, serviceName, role.Labels[AkashManifestServiceLabelName]) + + // Verify AkashManagedLabelName is present + require.Contains(t, role.Labels, AkashManagedLabelName) + require.Equal(t, "true", role.Labels[AkashManagedLabelName]) + }) + + t.Run("RoleBinding has manifest-service label", func(t *testing.T) { + rbBuilder := BuildRoleBinding(workload) + rb, err := rbBuilder.Create() + require.NoError(t, err) + require.NotNil(t, rb) + + // Verify AkashManifestServiceLabelName is present + require.Contains(t, rb.Labels, AkashManifestServiceLabelName) + require.Equal(t, serviceName, rb.Labels[AkashManifestServiceLabelName]) + + // Verify AkashManagedLabelName is present + require.Contains(t, rb.Labels, AkashManagedLabelName) + require.Equal(t, "true", rb.Labels[AkashManagedLabelName]) + }) +} diff --git a/cluster/kube/builder/rolebinding.go b/cluster/kube/builder/rolebinding.go new file mode 100644 index 000000000..8ad2e61a9 --- /dev/null +++ b/cluster/kube/builder/rolebinding.go @@ -0,0 +1,89 @@ +package builder + +import ( + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// RoleBinding is a builder for creating or updating a Kubernetes RoleBinding. +// +// The builder always creates RoleBindings with: +// - RoleRef pointing to a Role of the same name in the same namespace +// - A single Subject referencing a ServiceAccount of the same name in the same namespace +// +// Note: Kubernetes does not allow updating RoleRef on an existing RoleBinding. +// If the RoleRef needs to change, the RoleBinding must be deleted and recreated. +type RoleBinding interface { + builderBase + Create() (*rbacv1.RoleBinding, error) + Update(obj *rbacv1.RoleBinding) (*rbacv1.RoleBinding, error) +} + +type roleBinding struct { + *Workload +} + +var _ RoleBinding = (*roleBinding)(nil) + +// BuildRoleBinding creates a new RoleBinding builder for the given workload. +func BuildRoleBinding(workload *Workload) RoleBinding { + return &roleBinding{ + Workload: workload, + } +} + +func (b *roleBinding) labels() map[string]string { + return AppendLeaseLabels(b.deployment.LeaseID(), b.Workload.labels()) +} + +// Create returns a new RoleBinding with the name, labels, RoleRef, and Subjects +// derived from the builder's workload. +// +// The RoleBinding will reference a Role and ServiceAccount with the same name +// in the same namespace as the workload. +func (b *roleBinding) Create() (*rbacv1.RoleBinding, error) { + return &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: b.Name(), + Labels: b.labels(), + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "Role", + Name: b.Name(), + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: b.Name(), + Namespace: b.NS(), + }, + }, + }, nil +} + +// Update modifies an existing RoleBinding by updating its labels to match the +// builder's workload. The RoleRef and Subjects are reset to their canonical +// values (pointing to a Role and ServiceAccount of the same name). +// +// Note: Since Kubernetes RoleRef is immutable, this method will fail at the +// API level if the caller attempts to change the RoleRef. The only meaningful +// change this method can apply is to labels. +func (b *roleBinding) Update(obj *rbacv1.RoleBinding) (*rbacv1.RoleBinding, error) { + uobj := obj.DeepCopy() + uobj.Name = b.Name() + uobj.Labels = updateAkashLabels(obj.Labels, b.labels()) + uobj.RoleRef = rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "Role", + Name: b.Name(), + } + uobj.Subjects = []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: b.Name(), + Namespace: b.NS(), + }, + } + return uobj, nil +} diff --git a/cluster/kube/builder/serviceaccount.go b/cluster/kube/builder/serviceaccount.go new file mode 100644 index 000000000..592ba49ba --- /dev/null +++ b/cluster/kube/builder/serviceaccount.go @@ -0,0 +1,44 @@ +package builder + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type ServiceAccount interface { + builderBase + Create() (*corev1.ServiceAccount, error) + Update(obj *corev1.ServiceAccount) (*corev1.ServiceAccount, error) +} + +type serviceAccount struct { + *Workload +} + +var _ ServiceAccount = (*serviceAccount)(nil) + +func BuildServiceAccount(workload *Workload) ServiceAccount { + return &serviceAccount{ + Workload: workload, + } +} + +func (b *serviceAccount) labels() map[string]string { + return AppendLeaseLabels(b.deployment.LeaseID(), b.Workload.labels()) +} + +func (b *serviceAccount) Create() (*corev1.ServiceAccount, error) { + return &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: b.Name(), + Labels: b.labels(), + }, + }, nil +} + +func (b *serviceAccount) Update(obj *corev1.ServiceAccount) (*corev1.ServiceAccount, error) { + uobj := obj.DeepCopy() + uobj.Name = b.Name() + uobj.Labels = updateAkashLabels(obj.Labels, b.labels()) + return uobj, nil +} diff --git a/cluster/kube/builder/statefulset.go b/cluster/kube/builder/statefulset.go index 91771c2f8..7e9365af2 100644 --- a/cluster/kube/builder/statefulset.go +++ b/cluster/kube/builder/statefulset.go @@ -65,7 +65,8 @@ func (b *statefulSet) Create() (*appsv1.StatefulSet, error) { // nolint:unparam SecurityContext: &corev1.PodSecurityContext{ RunAsNonRoot: &falseValue, }, - AutomountServiceAccountToken: &falseValue, + AutomountServiceAccountToken: b.automountServiceAccountToken(), + ServiceAccountName: b.serviceAccountName(), Containers: []corev1.Container{b.container()}, ImagePullSecrets: b.secretsRefs, Volumes: b.volumesObjs, @@ -87,6 +88,8 @@ func (b *statefulSet) Update(obj *appsv1.StatefulSet) (*appsv1.StatefulSet, erro uobj.Spec.Template.Labels = b.labels() uobj.Spec.Template.Spec.Affinity = b.affinity() uobj.Spec.Template.Spec.RuntimeClassName = b.runtimeClass() + uobj.Spec.Template.Spec.AutomountServiceAccountToken = b.automountServiceAccountToken() + uobj.Spec.Template.Spec.ServiceAccountName = b.serviceAccountName() uobj.Spec.Template.Spec.Containers = []corev1.Container{b.container()} uobj.Spec.Template.Spec.ImagePullSecrets = b.secretsRefs uobj.Spec.Template.Spec.Volumes = b.volumesObjs diff --git a/cluster/kube/builder/workload.go b/cluster/kube/builder/workload.go index d4c7b146d..48d2655a1 100644 --- a/cluster/kube/builder/workload.go +++ b/cluster/kube/builder/workload.go @@ -419,6 +419,72 @@ func (b *Workload) addEnvVarsForDeployment(envVarsAlreadyAdded map[string]int, e env = addIfNotPresent(envVarsAlreadyAdded, env, envVarAkashOwner, lid.Owner) env = addIfNotPresent(envVarsAlreadyAdded, env, envVarAkashProvider, lid.Provider) env = addIfNotPresent(envVarsAlreadyAdded, env, envVarAkashClusterPublicHostname, b.settings.ClusterPublicHostname) + env = addIfNotPresent(envVarsAlreadyAdded, env, envVarKubernetesNamespaceOverride, b.NS()) return env } + +// getWorkloadPermissions extracts all permission types from the service params +// and returns them as a WorkloadPermissions struct. +// NOTE: Currently only Read permissions are supported in the manifest SDK. +func (b *Workload) getWorkloadPermissions() *WorkloadPermissions { + service := &b.group.Services[b.serviceIdx] + if service.Params == nil || service.Params.Permissions == nil { + return nil + } + + perms := &WorkloadPermissions{ + Read: service.Params.Permissions.Read, + } + + return perms +} + +// hasPermissions returns true if any permissions are defined for this workload. +// This checks all permission types (read, write, delete). +func (b *Workload) hasPermissions() bool { + perms := b.getWorkloadPermissions() + return perms.HasAny() +} + +// getPermissions returns the read permissions list for backward compatibility. +// Deprecated: Use GetWorkloadPermissions() for access to all permission types. +func (b *Workload) getPermissions() []string { + perms := b.getWorkloadPermissions() + if perms == nil { + return nil + } + return perms.Read +} + +func (b *Workload) automountServiceAccountToken() *bool { + if b.hasPermissions() { + trueValue := true + return &trueValue + } + falseValue := false + return &falseValue +} + +func (b *Workload) serviceAccountName() string { + if b.hasPermissions() { + return b.Name() + } + return "" +} + +// HasPermissions returns true if any permissions are defined for this workload. +func (b *Workload) HasPermissions() bool { + return b.hasPermissions() +} + +// GetPermissions returns the read permissions list for backward compatibility. +// Deprecated: Use GetWorkloadPermissions() for access to all permission types. +func (b *Workload) GetPermissions() []string { + return b.getPermissions() +} + +// GetWorkloadPermissions returns all permissions for this workload organized by type. +func (b *Workload) GetWorkloadPermissions() *WorkloadPermissions { + return b.getWorkloadPermissions() +} diff --git a/cluster/kube/cleanup.go b/cluster/kube/cleanup.go index f3ee9aac8..37adc83f8 100644 --- a/cluster/kube/cleanup.go +++ b/cluster/kube/cleanup.go @@ -14,7 +14,7 @@ import ( "github.com/akash-network/provider/cluster/kube/builder" ) -func cleanupStaleResources(ctx context.Context, kc kubernetes.Interface, lid mtypes.LeaseID, group *mani.Group) error { +func cleanupStaleResources(ctx context.Context, kc kubernetes.Interface, lid mtypes.LeaseID, group *mani.Group, servicesWithPermissions []string) error { ns := builder.LidNS(lid) // build label selector for objects not in current manifest group @@ -66,5 +66,76 @@ func cleanupStaleResources(ctx context.Context, kc kubernetes.Interface, lid mty } } + // cleanup stale RBAC resources for services that no longer have permissions + if err := cleanupStaleRBACResources(ctx, kc, ns, servicesWithPermissions); err != nil { + return err + } + + return nil +} + +// cleanupStaleRBACResources removes ServiceAccounts, Roles, and RoleBindings +// that belong to services that no longer have permissions in the current manifest. +// This handles two cases: +// 1. A service is removed from the manifest entirely +// 2. A service still exists but its permissions are removed +func cleanupStaleRBACResources(ctx context.Context, kc kubernetes.Interface, ns string, servicesWithPermissions []string) error { + selector := labels.NewSelector() + + if len(servicesWithPermissions) > 0 { + req, err := labels.NewRequirement(builder.AkashManifestServiceLabelName, selection.NotIn, servicesWithPermissions) + if err != nil { + return err + } + selector = selector.Add(*req) + } + + reqManaged, err := labels.NewRequirement(builder.AkashManagedLabelName, selection.Equals, []string{"true"}) + if err != nil { + return err + } + selector = selector.Add(*reqManaged) + + rbacSelector := selector.String() + + // delete stale service accounts + serviceAccounts, err := kc.CoreV1().ServiceAccounts(ns).List(ctx, metav1.ListOptions{ + LabelSelector: rbacSelector, + }) + if err != nil { + return err + } + for _, sa := range serviceAccounts.Items { + if err := kc.CoreV1().ServiceAccounts(ns).Delete(ctx, sa.Name, metav1.DeleteOptions{}); err != nil { + return err + } + } + + // delete stale roles + roles, err := kc.RbacV1().Roles(ns).List(ctx, metav1.ListOptions{ + LabelSelector: rbacSelector, + }) + if err != nil { + return err + } + for _, role := range roles.Items { + if err := kc.RbacV1().Roles(ns).Delete(ctx, role.Name, metav1.DeleteOptions{}); err != nil { + return err + } + } + + // delete stale role bindings + roleBindings, err := kc.RbacV1().RoleBindings(ns).List(ctx, metav1.ListOptions{ + LabelSelector: rbacSelector, + }) + if err != nil { + return err + } + for _, rb := range roleBindings.Items { + if err := kc.RbacV1().RoleBindings(ns).Delete(ctx, rb.Name, metav1.DeleteOptions{}); err != nil { + return err + } + } + return nil } diff --git a/cluster/kube/cleanup_test.go b/cluster/kube/cleanup_test.go new file mode 100644 index 000000000..4a2d51717 --- /dev/null +++ b/cluster/kube/cleanup_test.go @@ -0,0 +1,297 @@ +package kube + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" + + mani "pkg.akt.dev/go/manifest/v2beta3" + "pkg.akt.dev/go/testutil" + + "github.com/akash-network/provider/cluster/kube/builder" +) + +func TestCleanupStaleRBACResources(t *testing.T) { + ctx := context.Background() + lid := testutil.LeaseID(t) + ns := builder.LidNS(lid) + + // Create labels for a stale service that no longer has permissions + staleServiceLabels := map[string]string{ + builder.AkashManagedLabelName: "true", + builder.AkashManifestServiceLabelName: "old-service", + } + builder.AppendLeaseLabels(lid, staleServiceLabels) + + // Create labels for a current service that still has permissions + currentServiceLabels := map[string]string{ + builder.AkashManagedLabelName: "true", + builder.AkashManifestServiceLabelName: "current-service", + } + builder.AppendLeaseLabels(lid, currentServiceLabels) + + // Create stale RBAC resources (for old-service which no longer has permissions) + staleServiceAccount := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "old-service", + Namespace: ns, + Labels: staleServiceLabels, + }, + } + + staleRole := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "old-service", + Namespace: ns, + Labels: staleServiceLabels, + }, + } + + staleRoleBinding := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "old-service", + Namespace: ns, + Labels: staleServiceLabels, + }, + } + + // Create current RBAC resources (for current-service which still has permissions) + currentServiceAccount := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "current-service", + Namespace: ns, + Labels: currentServiceLabels, + }, + } + + currentRole := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "current-service", + Namespace: ns, + Labels: currentServiceLabels, + }, + } + + currentRoleBinding := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "current-service", + Namespace: ns, + Labels: currentServiceLabels, + }, + } + + kc := fake.NewClientset( + staleServiceAccount, staleRole, staleRoleBinding, + currentServiceAccount, currentRole, currentRoleBinding, + ) + + // Only "current-service" still has permissions + servicesWithPermissions := []string{"current-service"} + + // Run cleanup + err := cleanupStaleRBACResources(ctx, kc, ns, servicesWithPermissions) + require.NoError(t, err) + + // Verify stale resources are deleted + serviceAccounts, err := kc.CoreV1().ServiceAccounts(ns).List(ctx, metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, serviceAccounts.Items, 1) + require.Equal(t, "current-service", serviceAccounts.Items[0].Name) + + roles, err := kc.RbacV1().Roles(ns).List(ctx, metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, roles.Items, 1) + require.Equal(t, "current-service", roles.Items[0].Name) + + roleBindings, err := kc.RbacV1().RoleBindings(ns).List(ctx, metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, roleBindings.Items, 1) + require.Equal(t, "current-service", roleBindings.Items[0].Name) +} + +func TestCleanupStaleRBACResourcesDoesNotDeleteNonAkashResources(t *testing.T) { + ctx := context.Background() + lid := testutil.LeaseID(t) + ns := builder.LidNS(lid) + + // Create a non-Akash managed ServiceAccount (no akash.network label) + nonAkashServiceAccount := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "system-service-account", + Namespace: ns, + Labels: map[string]string{ + "app": "system", + }, + }, + } + + // Create labels for a stale service + staleServiceLabels := map[string]string{ + builder.AkashManagedLabelName: "true", + builder.AkashManifestServiceLabelName: "old-service", + } + builder.AppendLeaseLabels(lid, staleServiceLabels) + + staleServiceAccount := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "old-service", + Namespace: ns, + Labels: staleServiceLabels, + }, + } + + kc := fake.NewClientset(nonAkashServiceAccount, staleServiceAccount) + + // No services currently have permissions + servicesWithPermissions := []string{} + + // Run cleanup + err := cleanupStaleRBACResources(ctx, kc, ns, servicesWithPermissions) + require.NoError(t, err) + + // Verify non-Akash resource is NOT deleted, but stale Akash resource IS deleted + serviceAccounts, err := kc.CoreV1().ServiceAccounts(ns).List(ctx, metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, serviceAccounts.Items, 1) + require.Equal(t, "system-service-account", serviceAccounts.Items[0].Name) +} + +func TestCleanupRBACWhenPermissionsRemoved(t *testing.T) { + // This tests the specific case where a service STILL EXISTS in the manifest + // but its permissions have been removed + ctx := context.Background() + lid := testutil.LeaseID(t) + ns := builder.LidNS(lid) + + // Create labels for "web" service which previously had permissions + webServiceLabels := map[string]string{ + builder.AkashManagedLabelName: "true", + builder.AkashManifestServiceLabelName: "web", + } + builder.AppendLeaseLabels(lid, webServiceLabels) + + // RBAC resources created when "web" had permissions + webServiceAccount := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "web", + Namespace: ns, + Labels: webServiceLabels, + }, + } + + webRole := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "web", + Namespace: ns, + Labels: webServiceLabels, + }, + } + + webRoleBinding := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "web", + Namespace: ns, + Labels: webServiceLabels, + }, + } + + kc := fake.NewClientset(webServiceAccount, webRole, webRoleBinding) + + // Manifest still has "web" service, but it no longer has permissions + // So servicesWithPermissions is empty + group := &mani.Group{ + Services: mani.Services{ + {Name: "web"}, // Service still exists + }, + } + servicesWithPermissions := []string{} // But no longer has permissions + + // Run the full cleanup + err := cleanupStaleResources(ctx, kc, lid, group, servicesWithPermissions) + require.NoError(t, err) + + // Verify RBAC resources for "web" are deleted even though the service still exists + serviceAccounts, err := kc.CoreV1().ServiceAccounts(ns).List(ctx, metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, serviceAccounts.Items, 0) + + roles, err := kc.RbacV1().Roles(ns).List(ctx, metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, roles.Items, 0) + + roleBindings, err := kc.RbacV1().RoleBindings(ns).List(ctx, metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, roleBindings.Items, 0) +} + +func TestCleanupStaleResourcesIntegration(t *testing.T) { + ctx := context.Background() + lid := testutil.LeaseID(t) + ns := builder.LidNS(lid) + + // Create labels for a removed service + staleServiceLabels := map[string]string{ + builder.AkashManagedLabelName: "true", + builder.AkashManifestServiceLabelName: "removed-service", + } + builder.AppendLeaseLabels(lid, staleServiceLabels) + + // Create stale RBAC resources for the removed service + staleServiceAccount := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "removed-service", + Namespace: ns, + Labels: staleServiceLabels, + }, + } + + staleRole := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: "removed-service", + Namespace: ns, + Labels: staleServiceLabels, + }, + } + + staleRoleBinding := &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "removed-service", + Namespace: ns, + Labels: staleServiceLabels, + }, + } + + kc := fake.NewClientset(staleServiceAccount, staleRole, staleRoleBinding) + + // Manifest group with only "active-service" (removed-service no longer exists) + group := &mani.Group{ + Services: mani.Services{ + {Name: "active-service"}, + }, + } + + // active-service has no permissions either + servicesWithPermissions := []string{} + + // Run the full cleanup + err := cleanupStaleResources(ctx, kc, lid, group, servicesWithPermissions) + require.NoError(t, err) + + // Verify all stale RBAC resources are deleted + serviceAccounts, err := kc.CoreV1().ServiceAccounts(ns).List(ctx, metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, serviceAccounts.Items, 0) + + roles, err := kc.RbacV1().Roles(ns).List(ctx, metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, roles.Items, 0) + + roleBindings, err := kc.RbacV1().RoleBindings(ns).List(ctx, metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, roleBindings.Items, 0) +} diff --git a/cluster/kube/client.go b/cluster/kube/client.go index 4182df178..b68a627ab 100644 --- a/cluster/kube/client.go +++ b/cluster/kube/client.go @@ -17,6 +17,7 @@ import ( corev1 "k8s.io/api/core/v1" eventsv1 "k8s.io/api/events/v1" netv1 "k8s.io/api/networking/v1" + rbacv1 "k8s.io/api/rbac/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/version" @@ -191,11 +192,14 @@ func (c *client) Deployments(ctx context.Context) ([]ctypes.IDeployment, error) } type deploymentService struct { - deployment builder.Deployment - statefulSet builder.StatefulSet - localService builder.Service - globalService builder.Service - credentials builder.ServiceCredentials + deployment builder.Deployment + statefulSet builder.StatefulSet + localService builder.Service + globalService builder.Service + credentials builder.ServiceCredentials + serviceAccount builder.ServiceAccount + role builder.Role + roleBinding builder.RoleBinding } type deploymentApplies struct { @@ -206,30 +210,39 @@ type deploymentApplies struct { } type previousObj struct { - nmani *crd.Manifest - umani *crd.Manifest - omani *crd.Manifest - nns *corev1.Namespace - uns *corev1.Namespace - ons *corev1.Namespace - nNetPolicies []netv1.NetworkPolicy - uNetPolicies []netv1.NetworkPolicy - oNetPolicies []netv1.NetworkPolicy - nServiceCreds []*corev1.Secret - uServiceCreds []*corev1.Secret - oServiceCreds []*corev1.Secret - nStatefulSets []*appsv1.StatefulSet - uStatefulSets []*appsv1.StatefulSet - oStatefulSets []*appsv1.StatefulSet - nDeployments []*appsv1.Deployment - uDeployments []*appsv1.Deployment - oDeployments []*appsv1.Deployment - nLocalServices []*corev1.Service - uLocalServices []*corev1.Service - oLocalServices []*corev1.Service - nGlobalServices []*corev1.Service - uGlobalServices []*corev1.Service - oGlobalServices []*corev1.Service + nmani *crd.Manifest + umani *crd.Manifest + omani *crd.Manifest + nns *corev1.Namespace + uns *corev1.Namespace + ons *corev1.Namespace + nNetPolicies []netv1.NetworkPolicy + uNetPolicies []netv1.NetworkPolicy + oNetPolicies []netv1.NetworkPolicy + nServiceAccounts []*corev1.ServiceAccount + uServiceAccounts []*corev1.ServiceAccount + oServiceAccounts []*corev1.ServiceAccount + nRoles []*rbacv1.Role + uRoles []*rbacv1.Role + oRoles []*rbacv1.Role + nRoleBindings []*rbacv1.RoleBinding + uRoleBindings []*rbacv1.RoleBinding + oRoleBindings []*rbacv1.RoleBinding + nServiceCreds []*corev1.Secret + uServiceCreds []*corev1.Secret + oServiceCreds []*corev1.Secret + nStatefulSets []*appsv1.StatefulSet + uStatefulSets []*appsv1.StatefulSet + oStatefulSets []*appsv1.StatefulSet + nDeployments []*appsv1.Deployment + uDeployments []*appsv1.Deployment + oDeployments []*appsv1.Deployment + nLocalServices []*corev1.Service + uLocalServices []*corev1.Service + oLocalServices []*corev1.Service + nGlobalServices []*corev1.Service + uGlobalServices []*corev1.Service + oGlobalServices []*corev1.Service } func (p *previousObj) recover(ctx context.Context, kc kubernetes.Interface, ac akashclient.Interface) []error { @@ -271,6 +284,42 @@ func (p *previousObj) recover(ctx context.Context, kc kubernetes.Interface, ac a } } + for _, val := range slices.Backward(p.nRoleBindings) { + if err := kc.RbacV1().RoleBindings(val.Namespace).Delete(ctx, val.Name, metav1.DeleteOptions{}); err != nil { + errs = append(errs, err) + } + } + + for _, val := range slices.Backward(p.oRoleBindings) { + if _, err := kc.RbacV1().RoleBindings(val.Namespace).Update(ctx, val, metav1.UpdateOptions{}); err != nil { + errs = append(errs, err) + } + } + + for _, val := range slices.Backward(p.nRoles) { + if err := kc.RbacV1().Roles(val.Namespace).Delete(ctx, val.Name, metav1.DeleteOptions{}); err != nil { + errs = append(errs, err) + } + } + + for _, val := range slices.Backward(p.oRoles) { + if _, err := kc.RbacV1().Roles(val.Namespace).Update(ctx, val, metav1.UpdateOptions{}); err != nil { + errs = append(errs, err) + } + } + + for _, val := range slices.Backward(p.nServiceAccounts) { + if err := kc.CoreV1().ServiceAccounts(val.Namespace).Delete(ctx, val.Name, metav1.DeleteOptions{}); err != nil { + errs = append(errs, err) + } + } + + for _, val := range slices.Backward(p.oServiceAccounts) { + if _, err := kc.CoreV1().ServiceAccounts(val.Namespace).Update(ctx, val, metav1.UpdateOptions{}); err != nil { + errs = append(errs, err) + } + } + for _, val := range slices.Backward(p.nServiceCreds) { if err := kc.CoreV1().Secrets(val.Namespace).Delete(ctx, val.Name, metav1.DeleteOptions{}); err != nil { errs = append(errs, err) @@ -463,6 +512,9 @@ func (c *client) Deploy(ctx context.Context, deployment ctypes.IDeployment) (err applies.ns = builder.BuildNS(settings, cdeployment) applies.netPol = builder.BuildNetPol(settings, cdeployment) + // Track services that have permissions for RBAC cleanup + servicesWithPermissions := make([]string, 0) + for svcIdx := range group.Services { workload, err := builder.NewWorkloadBuilder(c.log, settings, cdeployment, currManifest, svcIdx) if err != nil { @@ -491,6 +543,17 @@ func (c *client) Deploy(ctx context.Context, deployment ctypes.IDeployment) (err svc.deployment = builder.NewDeployment(workload) } + // Create ServiceAccount, Role, and RoleBinding if permissions are defined + if workload.HasPermissions() { + c.log.Info("creating ServiceAccount, Role, and RoleBinding for service", "lease", lid, "service", service.Name, "permissions", workload.GetPermissions()) + svc.serviceAccount = builder.BuildServiceAccount(workload) + svc.role = builder.BuildRole(workload) + svc.roleBinding = builder.BuildRoleBinding(workload) + servicesWithPermissions = append(servicesWithPermissions, service.Name) + } else { + c.log.Debug("skipping ServiceAccount creation - no permissions defined", "lease", lid, "service", service.Name) + } + applies.services = append(applies.services, svc) if len(service.Expose) == 0 { @@ -514,7 +577,7 @@ func (c *client) Deploy(ctx context.Context, deployment ctypes.IDeployment) (err return err } - if err = cleanupStaleResources(ctx, c.kc, lid, group); err != nil { + if err = cleanupStaleResources(ctx, c.kc, lid, group, servicesWithPermissions); err != nil { c.log.Error("cleaning stale resources", "err", err, "lease", lid) return err } @@ -556,6 +619,62 @@ func (c *client) Deploy(ctx context.Context, deployment ctypes.IDeployment) (err applyObjs := applies.services[svcIdx] service := &group.Services[svcIdx] + // Create ServiceAccount, Role, and RoleBinding BEFORE deployment/statefulSet + // so they exist when the pod is created + if applyObjs.serviceAccount != nil { + nobj, uobj, oobj, err := applyServiceAccount(ctx, c.kc, applyObjs.serviceAccount) + if err != nil { + c.log.Error("applying service account", "err", err, "lease", lid, "service", service.Name) + return err + } + + if nobj != nil { + po.nServiceAccounts = append(po.nServiceAccounts, nobj) + } + if uobj != nil { + po.uServiceAccounts = append(po.uServiceAccounts, uobj) + } + if oobj != nil { + po.oServiceAccounts = append(po.oServiceAccounts, oobj) + } + } + + if applyObjs.role != nil { + nobj, uobj, oobj, err := applyRole(ctx, c.kc, applyObjs.role) + if err != nil { + c.log.Error("applying role", "err", err, "lease", lid, "service", service.Name) + return err + } + + if nobj != nil { + po.nRoles = append(po.nRoles, nobj) + } + if uobj != nil { + po.uRoles = append(po.uRoles, uobj) + } + if oobj != nil { + po.oRoles = append(po.oRoles, oobj) + } + } + + if applyObjs.roleBinding != nil { + nobj, uobj, oobj, err := applyRoleBinding(ctx, c.kc, applyObjs.roleBinding) + if err != nil { + c.log.Error("applying role binding", "err", err, "lease", lid, "service", service.Name) + return err + } + + if nobj != nil { + po.nRoleBindings = append(po.nRoleBindings, nobj) + } + if uobj != nil { + po.uRoleBindings = append(po.uRoleBindings, uobj) + } + if oobj != nil { + po.oRoleBindings = append(po.oRoleBindings, oobj) + } + } + if applyObjs.credentials != nil { nobj, uobj, obj, err := applyServiceCredentials(ctx, c.kc, applyObjs.credentials) if err != nil { diff --git a/cluster/kube/operators/clients/metallb/client.go b/cluster/kube/operators/clients/metallb/client.go index e75769140..888f68131 100644 --- a/cluster/kube/operators/clients/metallb/client.go +++ b/cluster/kube/operators/clients/metallb/client.go @@ -13,6 +13,7 @@ import ( "sync" "github.com/prometheus/common/expfmt" + "github.com/prometheus/common/model" corev1 "k8s.io/api/core/v1" kubeErrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -141,7 +142,7 @@ func (c *client) GetIPAddressUsage(ctx context.Context) (uint, uint, error) { return math.MaxUint32, math.MaxUint32, fmt.Errorf("%w: response status %d", errMetalLB, response.StatusCode) } - var parser expfmt.TextParser + parser := expfmt.NewTextParser(model.LegacyValidation) mf, err := parser.TextToMetricFamilies(response.Body) if err != nil { return math.MaxUint32, math.MaxUint32, err diff --git a/cluster/service.go b/cluster/service.go index a20c15b6d..94341dccb 100644 --- a/cluster/service.go +++ b/cluster/service.go @@ -75,8 +75,6 @@ type checkDeploymentExistsRequest struct { } // Cluster is the interface that wraps Reserve and Unreserve methods -// -//go:generate mockery --name Cluster type Cluster interface { Reserve(mtypes.OrderID, dtypes.ResourceGroup) (ctypes.Reservation, error) Unreserve(mtypes.OrderID) error @@ -90,8 +88,6 @@ type StatusClient interface { } // Service manage compute cluster for the provider. Will eventually integrate with kubernetes, etc... -// -//go:generate mockery --name Service type Service interface { StatusClient Cluster diff --git a/go.mod b/go.mod index 5eda044c6..54b88aa44 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/blang/semver/v4 v4.0.0 github.com/boz/go-lifecycle v0.1.1 github.com/cometbft/cometbft v0.38.21 - github.com/cosmos/cosmos-sdk v0.53.3 + github.com/cosmos/cosmos-sdk v0.53.5 github.com/desertbit/timer v1.0.1 github.com/fsnotify/fsnotify v1.9.0 github.com/go-acme/lego/v4 v4.26.0 @@ -26,14 +26,14 @@ require ( github.com/hashicorp/go-retryablehttp v0.7.8 github.com/jaypipes/ghw v0.19.1 github.com/moby/term v0.5.2 - github.com/prometheus/client_golang v1.23.0 - github.com/prometheus/common v0.65.0 + github.com/prometheus/client_golang v1.23.2 + github.com/prometheus/common v0.66.1 github.com/rakyll/statik v0.1.7 github.com/rook/rook v1.14.12 github.com/rook/rook/pkg/apis v0.0.0-20231204200402-5287527732f7 github.com/rs/zerolog v1.34.0 github.com/shopspring/decimal v1.4.0 - github.com/spf13/cobra v1.10.1 + github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 @@ -41,8 +41,8 @@ require ( github.com/vektra/mockery/v3 v3.5.4 go.etcd.io/bbolt v1.4.3 go.uber.org/zap v1.27.0 - golang.org/x/net v0.44.0 - golang.org/x/sync v0.17.0 + golang.org/x/net v0.47.0 + golang.org/x/sync v0.19.0 google.golang.org/grpc v1.75.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v3 v3.0.1 @@ -56,7 +56,7 @@ require ( pkg.akt.dev/go/sdl v0.1.9 pkg.akt.dev/node v1.2.0-rc1 sigs.k8s.io/kind v0.30.0 - sigs.k8s.io/structured-merge-diff/v4 v4.7.0 + sigs.k8s.io/structured-merge-diff/v4 v4.6.0 software.sslmate.com/src/go-pkcs12 v0.6.0 ) @@ -135,6 +135,7 @@ require ( github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/aws/aws-sdk-go v1.51.7 // indirect @@ -144,8 +145,8 @@ require ( github.com/bits-and-blooms/bitset v1.24.3 // indirect github.com/brunoga/deep v1.2.4 // indirect github.com/bytedance/gopkg v0.1.3 // indirect - github.com/bytedance/sonic v1.14.2 // indirect - github.com/bytedance/sonic/loader v0.4.0 // indirect + github.com/bytedance/sonic v1.15.0 // indirect + github.com/bytedance/sonic/loader v0.5.0 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -170,8 +171,8 @@ require ( github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/gogoproto v1.7.2 // indirect - github.com/cosmos/iavl v1.2.2 // indirect - github.com/cosmos/ibc-go/v10 v10.3.0 // indirect + github.com/cosmos/iavl v1.2.6 // indirect + github.com/cosmos/ibc-go/v10 v10.5.0 // indirect github.com/cosmos/ics23/go v0.11.0 // indirect github.com/cosmos/ledger-cosmos-go v0.16.0 // indirect github.com/cosmos/rosetta v0.50.12 // indirect @@ -188,7 +189,7 @@ require ( github.com/emicklei/go-restful/v3 v3.11.3 // indirect github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect - github.com/ethereum/go-ethereum v1.15.11 // indirect + github.com/ethereum/go-ethereum v1.16.8 // indirect github.com/evanphx/json-patch/v5 v5.8.0 // indirect github.com/fatih/color v1.17.0 // indirect github.com/fatih/structs v1.1.0 // indirect @@ -211,7 +212,7 @@ require ( github.com/gogo/googleapis v1.4.1 // indirect github.com/gogo/protobuf v1.3.3 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/golang/snappy v0.0.5-0.20231225225746-43d5d4cd4e0e // indirect + github.com/golang/snappy v1.0.0 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/flatbuffers v25.2.10+incompatible // indirect github.com/google/gnostic-models v0.6.9 // indirect @@ -227,7 +228,7 @@ require ( github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-getter v1.7.8 // indirect + github.com/hashicorp/go-getter v1.7.9 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-metrics v0.5.4 // indirect @@ -286,7 +287,6 @@ require ( github.com/minio/highwayhash v1.0.3 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect @@ -325,7 +325,7 @@ require ( github.com/tendermint/go-amino v0.16.0 // indirect github.com/tidwall/btree v1.7.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ulikunitz/xz v0.5.11 // indirect + github.com/ulikunitz/xz v0.5.14 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -350,15 +350,15 @@ require ( go.yaml.in/yaml/v2 v2.4.2 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/arch v0.17.0 // indirect - golang.org/x/crypto v0.42.0 // indirect + golang.org/x/crypto v0.45.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect - golang.org/x/mod v0.27.0 // indirect + golang.org/x/mod v0.29.0 // indirect golang.org/x/oauth2 v0.31.0 // indirect - golang.org/x/sys v0.36.0 // indirect - golang.org/x/term v0.35.0 // indirect - golang.org/x/text v0.29.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect + golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.13.0 // indirect - golang.org/x/tools v0.36.0 // indirect + golang.org/x/tools v0.38.0 // indirect golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect google.golang.org/api v0.249.0 // indirect google.golang.org/genproto v0.0.0-20250728155136-f173205681a0 // indirect @@ -373,7 +373,7 @@ require ( k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect - nhooyr.io/websocket v1.8.11 // indirect + nhooyr.io/websocket v1.8.17 // indirect pgregory.net/rapid v1.2.0 // indirect pkg.akt.dev/specs v0.0.1 // indirect rsc.io/qr v0.2.0 // indirect diff --git a/go.sum b/go.sum index dab6e7f0b..9de1f90e1 100644 --- a/go.sum +++ b/go.sum @@ -1303,6 +1303,8 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0 github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= @@ -1399,8 +1401,8 @@ github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= -github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= -github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= +github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -1505,10 +1507,10 @@ github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE= github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ4GUkT+tbFI= -github.com/cosmos/iavl v1.2.2 h1:qHhKW3I70w+04g5KdsdVSHRbFLgt3yY3qTMd4Xa4rC8= -github.com/cosmos/iavl v1.2.2/go.mod h1:GiM43q0pB+uG53mLxLDzimxM9l/5N9UuSY3/D0huuVw= -github.com/cosmos/ibc-go/v10 v10.3.0 h1:w5DkHih8qn15deAeFoTk778WJU+xC1krJ5kDnicfUBc= -github.com/cosmos/ibc-go/v10 v10.3.0/go.mod h1:CthaR7n4d23PJJ7wZHegmNgbVcLXCQql7EwHrAXnMtw= +github.com/cosmos/iavl v1.2.6 h1:Hs3LndJbkIB+rEvToKJFXZvKo6Vy0Ex1SJ54hhtioIs= +github.com/cosmos/iavl v1.2.6/go.mod h1:GiM43q0pB+uG53mLxLDzimxM9l/5N9UuSY3/D0huuVw= +github.com/cosmos/ibc-go/v10 v10.5.0 h1:NI+cX04fXdu9JfP0V0GYeRi1ENa7PPdq0BYtVYo8Zrs= +github.com/cosmos/ibc-go/v10 v10.5.0/go.mod h1:a74pAPUSJ7NewvmvELU74hUClJhwnmm5MGbEaiTw/kE= github.com/cosmos/ics23/go v0.11.0 h1:jk5skjT0TqX5e5QJbEnwXIS2yI2vnmLOgpQPeM5RtnU= github.com/cosmos/ics23/go v0.11.0/go.mod h1:A8OjxPE67hHST4Icw94hOxxFEJMBG031xIGF/JHNIY0= github.com/cosmos/keyring v1.2.0 h1:8C1lBP9xhImmIabyXW4c3vFjjLiBdGCmfLUfeZlV1Yo= @@ -1610,8 +1612,8 @@ github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7 github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8= github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= -github.com/ethereum/go-ethereum v1.15.11 h1:JK73WKeu0WC0O1eyX+mdQAVHUV+UR1a9VB/domDngBU= -github.com/ethereum/go-ethereum v1.15.11/go.mod h1:mf8YiHIb0GR4x4TipcvBUPxJLw1mFdmxzoDi11sDRoI= +github.com/ethereum/go-ethereum v1.16.8 h1:LLLfkZWijhR5m6yrAXbdlTeXoqontH+Ga2f9igY7law= +github.com/ethereum/go-ethereum v1.16.8/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -1832,8 +1834,8 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.5-0.20231225225746-43d5d4cd4e0e h1:4bw4WeyTYPp0smaXiJZCNnLrvVBqirQVreixayXezGc= -github.com/golang/snappy v0.0.5-0.20231225225746-43d5d4cd4e0e/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= @@ -1997,8 +1999,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-getter v1.7.8 h1:mshVHx1Fto0/MydBekWan5zUipGq7jO0novchgMmSiY= -github.com/hashicorp/go-getter v1.7.8/go.mod h1:2c6CboOEb9jG6YvmC9xdD+tyAFsrUaJPedwXDGr0TM4= +github.com/hashicorp/go-getter v1.7.9 h1:G9gcjrDixz7glqJ+ll5IWvggSBR+R0B54DSRt4qfdC4= +github.com/hashicorp/go-getter v1.7.9/go.mod h1:dyFCmT1AQkDfOIt9NH8pw9XBDqNrIKJT5ylbpi7zPNE= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= @@ -2261,8 +2263,6 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= @@ -2435,8 +2435,8 @@ github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeD github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -2458,8 +2458,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= +github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -2553,8 +2553,8 @@ github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qq github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= -github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -2620,8 +2620,8 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= -github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.14 h1:uv/0Bq533iFdnMHZdRBTOlaNMdb1+ZxXIlHDZHIHcvg= +github.com/ulikunitz/xz v0.5.14/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= @@ -2779,8 +2779,8 @@ golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1m golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= -golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb h1:xIApU0ow1zwMa2uL1VDNeQlNVFTWMQxZUZCMDy0Q4Us= golang.org/x/exp v0.0.0-20230711153332-06a737ee72cb/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= @@ -2826,8 +2826,8 @@ golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -2921,8 +2921,8 @@ golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2984,8 +2984,8 @@ golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -3118,8 +3118,8 @@ golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -3144,8 +3144,8 @@ golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= -golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= -golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -3170,8 +3170,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -3268,8 +3268,8 @@ golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY= golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= @@ -3870,8 +3870,8 @@ modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0= -nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= +nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y= +nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA= pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= pkg.akt.dev/go v0.1.9 h1:mJnPp1hxLrkSK/hV7muP9xXsRKdV19pLD1XFZ1og8m4= @@ -3910,8 +3910,8 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= -sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/testing_frameworks v0.1.1/go.mod h1:VVBKrHmJ6Ekkfz284YKhQePcdycOzNH9qL6ht1zEr/U= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/pkg/apis/akash.network/crd.yaml b/pkg/apis/akash.network/crd.yaml index b91b45537..d26dfd39c 100644 --- a/pkg/apis/akash.network/crd.yaml +++ b/pkg/apis/akash.network/crd.yaml @@ -200,6 +200,14 @@ spec: type: boolean mount: type: string + permissions: + type: object + nullable: true + properties: + read: + type: array + items: + type: string scheduler_params: type: object nullable: true @@ -365,6 +373,14 @@ spec: type: boolean mount: type: string + permissions: + type: object + nullable: true + properties: + read: + type: array + items: + type: string --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/pkg/apis/akash.network/v2beta2/manifest.go b/pkg/apis/akash.network/v2beta2/manifest.go index 98b692948..a8e3fd5da 100644 --- a/pkg/apis/akash.network/v2beta2/manifest.go +++ b/pkg/apis/akash.network/v2beta2/manifest.go @@ -85,8 +85,17 @@ type ManifestStorageParams struct { ReadOnly bool `json:"readOnly" yaml:"readOnly"` } +type ManifestServicePermissions struct { + Read []string `json:"read,omitempty"` + // Write permissions (not yet implemented - reserved for future use) + Write []string `json:"write,omitempty"` + // Delete permissions (not yet implemented - reserved for future use) + Delete []string `json:"delete,omitempty"` +} + type ManifestServiceParams struct { - Storage []ManifestStorageParams `json:"storage,omitempty"` + Storage []ManifestStorageParams `json:"storage,omitempty"` + Permissions *ManifestServicePermissions `json:"permissions,omitempty"` } type SchedulerResourceGPU struct { @@ -280,6 +289,18 @@ func (ms *ManifestService) fromCRD() (mani.Service, error) { ReadOnly: storage.ReadOnly, }) } + + if ms.Params.Permissions != nil { + ams.Params.Permissions = &mani.ServicePermissions{ + Read: ms.Params.Permissions.Read, + // NOTE: Write and Delete are stored in ManifestServicePermissions + // but cannot be converted to mani.ServicePermissions until the + // upstream SDK (pkg.akt.dev/go/manifest/v2beta3) adds support. + // When that happens, uncomment and update the following: + // Write: ms.Params.Permissions.Write, + // Delete: ms.Params.Permissions.Delete, + } + } } return *ams, nil @@ -319,6 +340,17 @@ func manifestServiceFromProvider(ams mani.Service, schedulerParams *SchedulerPar ReadOnly: storage.ReadOnly, }) } + + if ams.Params.Permissions != nil { + ms.Params.Permissions = &ManifestServicePermissions{ + Read: ams.Params.Permissions.Read, + // NOTE: Write and Delete will be populated here once the upstream + // SDK (pkg.akt.dev/go/manifest/v2beta3) adds support for them. + // When that happens, uncomment and update the following: + // Write: ams.Params.Permissions.Write, + // Delete: ams.Params.Permissions.Delete, + } + } } if ams.Credentials != nil { diff --git a/pkg/apis/akash.network/v2beta2/zz_generated.deepcopy.go b/pkg/apis/akash.network/v2beta2/zz_generated.deepcopy.go index da14bc11e..a0236ae3a 100644 --- a/pkg/apis/akash.network/v2beta2/zz_generated.deepcopy.go +++ b/pkg/apis/akash.network/v2beta2/zz_generated.deepcopy.go @@ -502,6 +502,11 @@ func (in *ManifestServiceParams) DeepCopyInto(out *ManifestServiceParams) { *out = make([]ManifestStorageParams, len(*in)) copy(*out, *in) } + if in.Permissions != nil { + in, out := &in.Permissions, &out.Permissions + *out = new(ManifestServicePermissions) + (*in).DeepCopyInto(*out) + } return } @@ -515,6 +520,27 @@ func (in *ManifestServiceParams) DeepCopy() *ManifestServiceParams { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ManifestServicePermissions) DeepCopyInto(out *ManifestServicePermissions) { + *out = *in + if in.Read != nil { + in, out := &in.Read, &out.Read + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManifestServicePermissions. +func (in *ManifestServicePermissions) DeepCopy() *ManifestServicePermissions { + if in == nil { + return nil + } + out := new(ManifestServicePermissions) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ManifestSpec) DeepCopyInto(out *ManifestSpec) { *out = *in diff --git a/pkg/client/applyconfiguration/akash.network/v2beta2/manifestserviceparams.go b/pkg/client/applyconfiguration/akash.network/v2beta2/manifestserviceparams.go index 238fff5b8..97991561c 100644 --- a/pkg/client/applyconfiguration/akash.network/v2beta2/manifestserviceparams.go +++ b/pkg/client/applyconfiguration/akash.network/v2beta2/manifestserviceparams.go @@ -21,7 +21,8 @@ package v2beta2 // ManifestServiceParamsApplyConfiguration represents a declarative configuration of the ManifestServiceParams type for use // with apply. type ManifestServiceParamsApplyConfiguration struct { - Storage []ManifestStorageParamsApplyConfiguration `json:"storage,omitempty"` + Storage []ManifestStorageParamsApplyConfiguration `json:"storage,omitempty"` + Permissions *ManifestServicePermissionsApplyConfiguration `json:"permissions,omitempty"` } // ManifestServiceParamsApplyConfiguration constructs a declarative configuration of the ManifestServiceParams type for use with @@ -42,3 +43,11 @@ func (b *ManifestServiceParamsApplyConfiguration) WithStorage(values ...*Manifes } return b } + +// WithPermissions sets the Permissions field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Permissions field is set to the value of the last call. +func (b *ManifestServiceParamsApplyConfiguration) WithPermissions(value *ManifestServicePermissionsApplyConfiguration) *ManifestServiceParamsApplyConfiguration { + b.Permissions = value + return b +} diff --git a/pkg/client/applyconfiguration/akash.network/v2beta2/manifestservicepermissions.go b/pkg/client/applyconfiguration/akash.network/v2beta2/manifestservicepermissions.go new file mode 100644 index 000000000..03f8fa784 --- /dev/null +++ b/pkg/client/applyconfiguration/akash.network/v2beta2/manifestservicepermissions.go @@ -0,0 +1,41 @@ +/* +Copyright The Akash Network 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. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v2beta2 + +// ManifestServicePermissionsApplyConfiguration represents a declarative configuration of the ManifestServicePermissions type for use +// with apply. +type ManifestServicePermissionsApplyConfiguration struct { + Read []string `json:"read,omitempty"` +} + +// ManifestServicePermissionsApplyConfiguration constructs a declarative configuration of the ManifestServicePermissions type for use with +// apply. +func ManifestServicePermissions() *ManifestServicePermissionsApplyConfiguration { + return &ManifestServicePermissionsApplyConfiguration{} +} + +// WithRead adds the given value to the Read field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Read field. +func (b *ManifestServicePermissionsApplyConfiguration) WithRead(values ...string) *ManifestServicePermissionsApplyConfiguration { + for i := range values { + b.Read = append(b.Read, values[i]) + } + return b +} diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go index 686a8a056..d052103ec 100644 --- a/pkg/client/clientset/versioned/fake/clientset_generated.go +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -23,6 +23,7 @@ import ( clientset "github.com/akash-network/provider/pkg/client/clientset/versioned" akashv2beta2 "github.com/akash-network/provider/pkg/client/clientset/versioned/typed/akash.network/v2beta2" fakeakashv2beta2 "github.com/akash-network/provider/pkg/client/clientset/versioned/typed/akash.network/v2beta2/fake" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/discovery" @@ -50,9 +51,13 @@ func NewSimpleClientset(objects ...runtime.Object) *Clientset { cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} cs.AddReactor("*", "*", testing.ObjectReaction(o)) cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + var opts metav1.ListOptions + if watchActcion, ok := action.(testing.WatchActionImpl); ok { + opts = watchActcion.ListOptions + } gvr := action.GetResource() ns := action.GetNamespace() - watch, err := o.Watch(gvr, ns) + watch, err := o.Watch(gvr, ns, opts) if err != nil { return false, nil, err } @@ -99,9 +104,13 @@ func NewClientset(objects ...runtime.Object) *Clientset { cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} cs.AddReactor("*", "*", testing.ObjectReaction(o)) cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + var opts metav1.ListOptions + if watchActcion, ok := action.(testing.WatchActionImpl); ok { + opts = watchActcion.ListOptions + } gvr := action.GetResource() ns := action.GetNamespace() - watch, err := o.Watch(gvr, ns) + watch, err := o.Watch(gvr, ns, opts) if err != nil { return false, nil, err } diff --git a/pkg/client/clientset/versioned/typed/akash.network/v2beta2/akash.network_client.go b/pkg/client/clientset/versioned/typed/akash.network/v2beta2/akash.network_client.go index c1dd54033..864fe52b9 100644 --- a/pkg/client/clientset/versioned/typed/akash.network/v2beta2/akash.network_client.go +++ b/pkg/client/clientset/versioned/typed/akash.network/v2beta2/akash.network_client.go @@ -65,9 +65,7 @@ func (c *AkashV2beta2Client) ProviderLeasedIPs(namespace string) ProviderLeasedI // where httpClient was generated with rest.HTTPClientFor(c). func NewForConfig(c *rest.Config) (*AkashV2beta2Client, error) { config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } + setConfigDefaults(&config) httpClient, err := rest.HTTPClientFor(&config) if err != nil { return nil, err @@ -79,9 +77,7 @@ func NewForConfig(c *rest.Config) (*AkashV2beta2Client, error) { // Note the http client provided takes precedence over the configured transport values. func NewForConfigAndClient(c *rest.Config, h *http.Client) (*AkashV2beta2Client, error) { config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } + setConfigDefaults(&config) client, err := rest.RESTClientForConfigAndClient(&config, h) if err != nil { return nil, err @@ -104,7 +100,7 @@ func New(c rest.Interface) *AkashV2beta2Client { return &AkashV2beta2Client{c} } -func setConfigDefaults(config *rest.Config) error { +func setConfigDefaults(config *rest.Config) { gv := akashnetworkv2beta2.SchemeGroupVersion config.GroupVersion = &gv config.APIPath = "/apis" @@ -113,8 +109,6 @@ func setConfigDefaults(config *rest.Config) error { if config.UserAgent == "" { config.UserAgent = rest.DefaultKubernetesUserAgent() } - - return nil } // RESTClient returns a RESTClient that is used to communicate diff --git a/pkg/client/informers/externalversions/akash.network/v2beta2/inventory.go b/pkg/client/informers/externalversions/akash.network/v2beta2/inventory.go index 32c7a8957..855ab2755 100644 --- a/pkg/client/informers/externalversions/akash.network/v2beta2/inventory.go +++ b/pkg/client/informers/externalversions/akash.network/v2beta2/inventory.go @@ -61,13 +61,25 @@ func NewFilteredInventoryInformer(client versioned.Interface, resyncPeriod time. if tweakListOptions != nil { tweakListOptions(&options) } - return client.AkashV2beta2().Inventories().List(context.TODO(), options) + return client.AkashV2beta2().Inventories().List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.AkashV2beta2().Inventories().Watch(context.TODO(), options) + return client.AkashV2beta2().Inventories().Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AkashV2beta2().Inventories().List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AkashV2beta2().Inventories().Watch(ctx, options) }, }, &apisakashnetworkv2beta2.Inventory{}, diff --git a/pkg/client/informers/externalversions/akash.network/v2beta2/inventoryrequest.go b/pkg/client/informers/externalversions/akash.network/v2beta2/inventoryrequest.go index aafb86113..25ff039af 100644 --- a/pkg/client/informers/externalversions/akash.network/v2beta2/inventoryrequest.go +++ b/pkg/client/informers/externalversions/akash.network/v2beta2/inventoryrequest.go @@ -61,13 +61,25 @@ func NewFilteredInventoryRequestInformer(client versioned.Interface, resyncPerio if tweakListOptions != nil { tweakListOptions(&options) } - return client.AkashV2beta2().InventoryRequests().List(context.TODO(), options) + return client.AkashV2beta2().InventoryRequests().List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.AkashV2beta2().InventoryRequests().Watch(context.TODO(), options) + return client.AkashV2beta2().InventoryRequests().Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AkashV2beta2().InventoryRequests().List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AkashV2beta2().InventoryRequests().Watch(ctx, options) }, }, &apisakashnetworkv2beta2.InventoryRequest{}, diff --git a/pkg/client/informers/externalversions/akash.network/v2beta2/manifest.go b/pkg/client/informers/externalversions/akash.network/v2beta2/manifest.go index cb87c7620..ca4b48fb4 100644 --- a/pkg/client/informers/externalversions/akash.network/v2beta2/manifest.go +++ b/pkg/client/informers/externalversions/akash.network/v2beta2/manifest.go @@ -62,13 +62,25 @@ func NewFilteredManifestInformer(client versioned.Interface, namespace string, r if tweakListOptions != nil { tweakListOptions(&options) } - return client.AkashV2beta2().Manifests(namespace).List(context.TODO(), options) + return client.AkashV2beta2().Manifests(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.AkashV2beta2().Manifests(namespace).Watch(context.TODO(), options) + return client.AkashV2beta2().Manifests(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AkashV2beta2().Manifests(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AkashV2beta2().Manifests(namespace).Watch(ctx, options) }, }, &apisakashnetworkv2beta2.Manifest{}, diff --git a/pkg/client/informers/externalversions/akash.network/v2beta2/providerhost.go b/pkg/client/informers/externalversions/akash.network/v2beta2/providerhost.go index 15e3c02c5..53434b195 100644 --- a/pkg/client/informers/externalversions/akash.network/v2beta2/providerhost.go +++ b/pkg/client/informers/externalversions/akash.network/v2beta2/providerhost.go @@ -62,13 +62,25 @@ func NewFilteredProviderHostInformer(client versioned.Interface, namespace strin if tweakListOptions != nil { tweakListOptions(&options) } - return client.AkashV2beta2().ProviderHosts(namespace).List(context.TODO(), options) + return client.AkashV2beta2().ProviderHosts(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.AkashV2beta2().ProviderHosts(namespace).Watch(context.TODO(), options) + return client.AkashV2beta2().ProviderHosts(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AkashV2beta2().ProviderHosts(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AkashV2beta2().ProviderHosts(namespace).Watch(ctx, options) }, }, &apisakashnetworkv2beta2.ProviderHost{}, diff --git a/pkg/client/informers/externalversions/akash.network/v2beta2/providerleasedip.go b/pkg/client/informers/externalversions/akash.network/v2beta2/providerleasedip.go index 57a6b7f9e..3d49243a6 100644 --- a/pkg/client/informers/externalversions/akash.network/v2beta2/providerleasedip.go +++ b/pkg/client/informers/externalversions/akash.network/v2beta2/providerleasedip.go @@ -62,13 +62,25 @@ func NewFilteredProviderLeasedIPInformer(client versioned.Interface, namespace s if tweakListOptions != nil { tweakListOptions(&options) } - return client.AkashV2beta2().ProviderLeasedIPs(namespace).List(context.TODO(), options) + return client.AkashV2beta2().ProviderLeasedIPs(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.AkashV2beta2().ProviderLeasedIPs(namespace).Watch(context.TODO(), options) + return client.AkashV2beta2().ProviderLeasedIPs(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AkashV2beta2().ProviderLeasedIPs(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.AkashV2beta2().ProviderLeasedIPs(namespace).Watch(ctx, options) }, }, &apisakashnetworkv2beta2.ProviderLeasedIP{}, diff --git a/testdata/sdl/permissions-events.yaml b/testdata/sdl/permissions-events.yaml new file mode 100644 index 000000000..1a24c338c --- /dev/null +++ b/testdata/sdl/permissions-events.yaml @@ -0,0 +1,46 @@ +--- +version: "2.0" +services: + log-collector: + image: ghcr.io/akash-network/log-collector + expose: + - port: 8080 + accept: + - ahostname.com + to: + - global: true + params: + permissions: + read: + - logs + - events +profiles: + compute: + log-collector: + resources: + cpu: + units: "100m" + memory: + size: "128Mi" + storage: + size: "1Gi" + placement: + westcoast: + attributes: + region: us-west + signedBy: + anyOf: + - 1 + - 2 + allOf: + - 3 + - 4 + pricing: + log-collector: + denom: uakt + amount: 50 +deployment: + log-collector: + westcoast: + profile: log-collector + count: 1 diff --git a/testdata/sdl/permissions.yaml b/testdata/sdl/permissions.yaml new file mode 100644 index 000000000..690a3bc83 --- /dev/null +++ b/testdata/sdl/permissions.yaml @@ -0,0 +1,68 @@ +--- +version: "2.0" +services: + web: + image: nginx + expose: + - port: 80 + accept: + - ahostname.com + to: + - global: true + params: + permissions: + read: + - logs + web2: + image: nginx + expose: + - port: 8080 + accept: + - bhostname.com + to: + - global: true +profiles: + compute: + web: + resources: + cpu: + units: "100m" + memory: + size: "128Mi" + storage: + size: "1Gi" + web2: + resources: + cpu: + units: "100m" + memory: + size: "128Mi" + storage: + size: "1Gi" + placement: + westcoast: + attributes: + region: us-west + signedBy: + anyOf: + - 1 + - 2 + allOf: + - 3 + - 4 + pricing: + web: + denom: uakt + amount: 50 + web2: + denom: uakt + amount: 50 +deployment: + web: + westcoast: + profile: web + count: 1 + web2: + westcoast: + profile: web2 + count: 1