diff --git a/staging/src/github.com/kcp-dev/cli/pkg/claims/cmd/cmd.go b/staging/src/github.com/kcp-dev/cli/pkg/claims/cmd/cmd.go index 370a7b042a4..f3ca5935223 100644 --- a/staging/src/github.com/kcp-dev/cli/pkg/claims/cmd/cmd.go +++ b/staging/src/github.com/kcp-dev/cli/pkg/claims/cmd/cmd.go @@ -27,7 +27,6 @@ import ( "github.com/kcp-dev/cli/pkg/claims/plugin" ) -// TODO: Add examples for edit and update claims. var ( claimsExample = ` # Lists the permission claims and their respective status related to a specific APIBinding. @@ -35,6 +34,21 @@ var ( # List permission claims and their respective status for all APIBindings in current workspace. %[1]s claims get apibinding + +# Accept a specific permission claim for an APIBinding. +%[1]s claims accept apibinding my-binding secrets + +# Accept a permission claim with a specific group (e.g., secrets in core group). +%[1]s claims accept apibinding my-binding secrets. + +# Accept all permission claims for an APIBinding. +%[1]s claims accept apibinding my-binding --all + +# Reject a specific permission claim for an APIBinding. +%[1]s claims reject apibinding my-binding configmaps + +# Reject all permission claims for an APIBinding. +%[1]s claims reject apibinding my-binding --all ` ) @@ -84,5 +98,66 @@ func New(streams genericclioptions.IOStreams) *cobra.Command { apibindingGetOpts.BindFlags(apibindingGetCmd) getcmd.AddCommand(apibindingGetCmd) claimsCmd.AddCommand(getcmd) + + // Accept command + acceptCmd := &cobra.Command{ + Use: "accept", + Short: "Accept permission claims for an APIBinding", + SilenceUsage: true, + TraverseChildren: true, + RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Help() + }, + } + + apibindingAcceptOpts := plugin.NewAcceptClaimOptions(streams) + apibindingAcceptCmd := &cobra.Command{ + Use: "apibinding ", + Short: "Accept a permission claim for an APIBinding", + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + if err := apibindingAcceptOpts.Complete(args); err != nil { + return err + } + if err := apibindingAcceptOpts.Validate(); err != nil { + return err + } + return apibindingAcceptOpts.Run(cmd.Context()) + }, + } + apibindingAcceptOpts.BindFlags(apibindingAcceptCmd) + acceptCmd.AddCommand(apibindingAcceptCmd) + claimsCmd.AddCommand(acceptCmd) + + // Reject command + rejectCmd := &cobra.Command{ + Use: "reject", + Short: "Reject permission claims for an APIBinding", + SilenceUsage: true, + TraverseChildren: true, + RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Help() + }, + } + + apibindingRejectOpts := plugin.NewRejectClaimOptions(streams) + apibindingRejectCmd := &cobra.Command{ + Use: "apibinding ", + Short: "Reject a permission claim for an APIBinding", + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + if err := apibindingRejectOpts.Complete(args); err != nil { + return err + } + if err := apibindingRejectOpts.Validate(); err != nil { + return err + } + return apibindingRejectOpts.Run(cmd.Context()) + }, + } + apibindingRejectOpts.BindFlags(apibindingRejectCmd) + rejectCmd.AddCommand(apibindingRejectCmd) + claimsCmd.AddCommand(rejectCmd) + return claimsCmd } diff --git a/staging/src/github.com/kcp-dev/cli/pkg/claims/plugin/accept.go b/staging/src/github.com/kcp-dev/cli/pkg/claims/plugin/accept.go new file mode 100644 index 00000000000..299f9b249de --- /dev/null +++ b/staging/src/github.com/kcp-dev/cli/pkg/claims/plugin/accept.go @@ -0,0 +1,271 @@ +/* +Copyright 2022 The KCP 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. +*/ + +package plugin + +import ( + "context" + "fmt" + "net/url" + "strings" + + "github.com/spf13/cobra" + + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + + "github.com/kcp-dev/cli/pkg/base" + pluginhelpers "github.com/kcp-dev/cli/pkg/helpers" + apishelpers "github.com/kcp-dev/cli/pkg/helpers/apis/apis" + "github.com/kcp-dev/sdk/apis/apis" + apisv1alpha2 "github.com/kcp-dev/sdk/apis/apis/v1alpha2" + kcpclientset "github.com/kcp-dev/sdk/client/clientset/versioned/cluster" +) + +// AcceptClaimOptions contains the options for accepting a permission claim. +type AcceptClaimOptions struct { + *base.Options + + // APIBindingName is the name of the APIBinding. + APIBindingName string + + // GroupResource is the group.resource of the claim to accept. + GroupResource string + + // IdentityHash is the optional identity hash for the claim. + IdentityHash string + + // All accepts all pending claims for the APIBinding. + All bool +} + +func NewAcceptClaimOptions(streams genericclioptions.IOStreams) *AcceptClaimOptions { + return &AcceptClaimOptions{ + Options: base.NewOptions(streams), + } +} + +func (a *AcceptClaimOptions) Complete(args []string) error { + if err := a.Options.Complete(); err != nil { + return err + } + + if len(args) > 0 { + a.APIBindingName = args[0] + } + if len(args) > 1 { + a.GroupResource = args[1] + } + return nil +} + +func (a *AcceptClaimOptions) Validate() error { + if a.APIBindingName == "" { + return fmt.Errorf("apibinding name is required") + } + if !a.All && a.GroupResource == "" { + return fmt.Errorf("group.resource is required (or use --all to accept all claims)") + } + return a.Options.Validate() +} + +func (a *AcceptClaimOptions) BindFlags(cmd *cobra.Command) { + a.Options.BindFlags(cmd) + cmd.Flags().StringVar(&a.IdentityHash, "identity-hash", "", "Identity hash of the claim (optional, used to disambiguate claims with the same group.resource)") + cmd.Flags().BoolVar(&a.All, "all", false, "Accept all pending permission claims for the APIBinding") +} + +func (a *AcceptClaimOptions) Run(ctx context.Context) error { + cfg, err := a.ClientConfig.ClientConfig() + if err != nil { + return err + } + + _, currentClusterName, err := pluginhelpers.ParseClusterURL(cfg.Host) + if err != nil { + return fmt.Errorf("current URL %q does not point to workspace", cfg.Host) + } + + preferredAPIBindingVersion, err := pluginhelpers.PreferredVersion(cfg, schema.GroupResource{ + Group: apis.GroupName, + Resource: "apibindings", + }) + if err != nil { + return fmt.Errorf("service discovery failed: %w", err) + } + + kcpClusterClient, err := newAcceptKCPClusterClient(a.ClientConfig) + if err != nil { + return fmt.Errorf("error while creating kcp client: %w", err) + } + + client := kcpClusterClient.Cluster(currentClusterName) + + binding, err := apishelpers.GetAPIBinding(ctx, client, preferredAPIBindingVersion, a.APIBindingName) + if err != nil { + return fmt.Errorf("error finding APIBinding %q: %w", a.APIBindingName, err) + } + + exportClaims := binding.GetExportPermissionClaims() + currentClaims := binding.GetPermissionClaims() + + if a.All { + // Accept all claims from the export + newClaims := acceptAllClaims(exportClaims, currentClaims) + if err := binding.SetPermissionClaims(newClaims); err != nil { + return fmt.Errorf("error setting permission claims: %w", err) + } + + if err := binding.Update(ctx, client); err != nil { + return fmt.Errorf("error updating APIBinding: %w", err) + } + + fmt.Fprintf(a.Out, "Accepted all permission claims for APIBinding %q\n", a.APIBindingName) + return nil + } + + // Parse the group.resource + gr := parseGroupResource(a.GroupResource) + + // Find and accept the specific claim + newClaims, found := acceptClaim(exportClaims, currentClaims, gr, a.IdentityHash) + if !found { + return fmt.Errorf("claim for %q not found in APIExport's permission claims", a.GroupResource) + } + + if err := binding.SetPermissionClaims(newClaims); err != nil { + return fmt.Errorf("error setting permission claims: %w", err) + } + + if err := binding.Update(ctx, client); err != nil { + return fmt.Errorf("error updating APIBinding: %w", err) + } + + fmt.Fprintf(a.Out, "Accepted permission claim %q for APIBinding %q\n", a.GroupResource, a.APIBindingName) + return nil +} + +func newAcceptKCPClusterClient(clientConfig clientcmd.ClientConfig) (kcpclientset.ClusterInterface, error) { + config, err := clientConfig.ClientConfig() + if err != nil { + return nil, err + } + clusterConfig := rest.CopyConfig(config) + u, err := url.Parse(config.Host) + if err != nil { + return nil, err + } + u.Path = "" + clusterConfig.Host = u.String() + clusterConfig.UserAgent = rest.DefaultKubernetesUserAgent() + return kcpclientset.NewForConfig(clusterConfig) +} + +func parseGroupResource(gr string) schema.GroupResource { + parts := strings.SplitN(gr, ".", 2) + if len(parts) == 1 { + // No group specified, assume core group + return schema.GroupResource{Resource: parts[0]} + } + return schema.GroupResource{Resource: parts[0], Group: parts[1]} +} + +func acceptAllClaims(exportClaims []apisv1alpha2.PermissionClaim, currentClaims []apisv1alpha2.AcceptablePermissionClaim) []apisv1alpha2.AcceptablePermissionClaim { + // Build a map of current claims by key + currentMap := make(map[string]apisv1alpha2.AcceptablePermissionClaim) + for _, claim := range currentClaims { + key := claimKey(claim.Group, claim.Resource, claim.IdentityHash) + currentMap[key] = claim + } + + // Accept all export claims + newClaims := make([]apisv1alpha2.AcceptablePermissionClaim, 0, len(exportClaims)) + for _, exportClaim := range exportClaims { + key := claimKey(exportClaim.Group, exportClaim.Resource, exportClaim.IdentityHash) + if existing, ok := currentMap[key]; ok { + // Update state to Accepted if not already + existing.State = apisv1alpha2.ClaimAccepted + newClaims = append(newClaims, existing) + } else { + // Create new accepted claim + newClaims = append(newClaims, apisv1alpha2.AcceptablePermissionClaim{ + ScopedPermissionClaim: apisv1alpha2.ScopedPermissionClaim{ + PermissionClaim: exportClaim, + Selector: apisv1alpha2.PermissionClaimSelector{ + MatchAll: true, + }, + }, + State: apisv1alpha2.ClaimAccepted, + }) + } + } + + return newClaims +} + +func acceptClaim(exportClaims []apisv1alpha2.PermissionClaim, currentClaims []apisv1alpha2.AcceptablePermissionClaim, gr schema.GroupResource, identityHash string) ([]apisv1alpha2.AcceptablePermissionClaim, bool) { + // Find the export claim + var targetClaim *apisv1alpha2.PermissionClaim + for i := range exportClaims { + claim := &exportClaims[i] + if claim.Resource == gr.Resource && claim.Group == gr.Group { + if identityHash == "" || claim.IdentityHash == identityHash { + targetClaim = claim + break + } + } + } + + if targetClaim == nil { + return nil, false + } + + // Build new claims list + key := claimKey(targetClaim.Group, targetClaim.Resource, targetClaim.IdentityHash) + found := false + newClaims := make([]apisv1alpha2.AcceptablePermissionClaim, 0, len(currentClaims)+1) + + for _, claim := range currentClaims { + claimK := claimKey(claim.Group, claim.Resource, claim.IdentityHash) + if claimK == key { + // Update existing claim to Accepted + claim.State = apisv1alpha2.ClaimAccepted + found = true + } + newClaims = append(newClaims, claim) + } + + if !found { + // Add new accepted claim + newClaims = append(newClaims, apisv1alpha2.AcceptablePermissionClaim{ + ScopedPermissionClaim: apisv1alpha2.ScopedPermissionClaim{ + PermissionClaim: *targetClaim, + Selector: apisv1alpha2.PermissionClaimSelector{ + MatchAll: true, + }, + }, + State: apisv1alpha2.ClaimAccepted, + }) + } + + return newClaims, true +} + +func claimKey(group, resource, identityHash string) string { + return fmt.Sprintf("%s.%s.%s", resource, group, identityHash) +} diff --git a/staging/src/github.com/kcp-dev/cli/pkg/claims/plugin/reject.go b/staging/src/github.com/kcp-dev/cli/pkg/claims/plugin/reject.go new file mode 100644 index 00000000000..da2184c0db1 --- /dev/null +++ b/staging/src/github.com/kcp-dev/cli/pkg/claims/plugin/reject.go @@ -0,0 +1,257 @@ +/* +Copyright 2022 The KCP 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. +*/ + +package plugin + +import ( + "context" + "fmt" + "net/url" + + "github.com/spf13/cobra" + + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + + "github.com/kcp-dev/cli/pkg/base" + pluginhelpers "github.com/kcp-dev/cli/pkg/helpers" + apishelpers "github.com/kcp-dev/cli/pkg/helpers/apis/apis" + "github.com/kcp-dev/sdk/apis/apis" + apisv1alpha2 "github.com/kcp-dev/sdk/apis/apis/v1alpha2" + kcpclientset "github.com/kcp-dev/sdk/client/clientset/versioned/cluster" +) + +// RejectClaimOptions contains the options for rejecting a permission claim. +type RejectClaimOptions struct { + *base.Options + + // APIBindingName is the name of the APIBinding. + APIBindingName string + + // GroupResource is the group.resource of the claim to reject. + GroupResource string + + // IdentityHash is the optional identity hash for the claim. + IdentityHash string + + // All rejects all pending claims for the APIBinding. + All bool +} + +func NewRejectClaimOptions(streams genericclioptions.IOStreams) *RejectClaimOptions { + return &RejectClaimOptions{ + Options: base.NewOptions(streams), + } +} + +func (r *RejectClaimOptions) Complete(args []string) error { + if err := r.Options.Complete(); err != nil { + return err + } + + if len(args) > 0 { + r.APIBindingName = args[0] + } + if len(args) > 1 { + r.GroupResource = args[1] + } + return nil +} + +func (r *RejectClaimOptions) Validate() error { + if r.APIBindingName == "" { + return fmt.Errorf("apibinding name is required") + } + if !r.All && r.GroupResource == "" { + return fmt.Errorf("group.resource is required (or use --all to reject all claims)") + } + return r.Options.Validate() +} + +func (r *RejectClaimOptions) BindFlags(cmd *cobra.Command) { + r.Options.BindFlags(cmd) + cmd.Flags().StringVar(&r.IdentityHash, "identity-hash", "", "Identity hash of the claim (optional, used to disambiguate claims with the same group.resource)") + cmd.Flags().BoolVar(&r.All, "all", false, "Reject all pending permission claims for the APIBinding") +} + +func (r *RejectClaimOptions) Run(ctx context.Context) error { + cfg, err := r.ClientConfig.ClientConfig() + if err != nil { + return err + } + + _, currentClusterName, err := pluginhelpers.ParseClusterURL(cfg.Host) + if err != nil { + return fmt.Errorf("current URL %q does not point to workspace", cfg.Host) + } + + preferredAPIBindingVersion, err := pluginhelpers.PreferredVersion(cfg, schema.GroupResource{ + Group: apis.GroupName, + Resource: "apibindings", + }) + if err != nil { + return fmt.Errorf("service discovery failed: %w", err) + } + + kcpClusterClient, err := newRejectKCPClusterClient(r.ClientConfig) + if err != nil { + return fmt.Errorf("error while creating kcp client: %w", err) + } + + client := kcpClusterClient.Cluster(currentClusterName) + + binding, err := apishelpers.GetAPIBinding(ctx, client, preferredAPIBindingVersion, r.APIBindingName) + if err != nil { + return fmt.Errorf("error finding APIBinding %q: %w", r.APIBindingName, err) + } + + exportClaims := binding.GetExportPermissionClaims() + currentClaims := binding.GetPermissionClaims() + + if r.All { + // Reject all claims from the export + newClaims := rejectAllClaims(exportClaims, currentClaims) + if err := binding.SetPermissionClaims(newClaims); err != nil { + return fmt.Errorf("error setting permission claims: %w", err) + } + + if err := binding.Update(ctx, client); err != nil { + return fmt.Errorf("error updating APIBinding: %w", err) + } + + fmt.Fprintf(r.Out, "Rejected all permission claims for APIBinding %q\n", r.APIBindingName) + return nil + } + + // Parse the group.resource + gr := parseGroupResource(r.GroupResource) + + // Find and reject the specific claim + newClaims, found := rejectClaim(exportClaims, currentClaims, gr, r.IdentityHash) + if !found { + return fmt.Errorf("claim for %q not found in APIExport's permission claims", r.GroupResource) + } + + if err := binding.SetPermissionClaims(newClaims); err != nil { + return fmt.Errorf("error setting permission claims: %w", err) + } + + if err := binding.Update(ctx, client); err != nil { + return fmt.Errorf("error updating APIBinding: %w", err) + } + + fmt.Fprintf(r.Out, "Rejected permission claim %q for APIBinding %q\n", r.GroupResource, r.APIBindingName) + return nil +} + +func newRejectKCPClusterClient(clientConfig clientcmd.ClientConfig) (kcpclientset.ClusterInterface, error) { + config, err := clientConfig.ClientConfig() + if err != nil { + return nil, err + } + clusterConfig := rest.CopyConfig(config) + u, err := url.Parse(config.Host) + if err != nil { + return nil, err + } + u.Path = "" + clusterConfig.Host = u.String() + clusterConfig.UserAgent = rest.DefaultKubernetesUserAgent() + return kcpclientset.NewForConfig(clusterConfig) +} + +func rejectAllClaims(exportClaims []apisv1alpha2.PermissionClaim, currentClaims []apisv1alpha2.AcceptablePermissionClaim) []apisv1alpha2.AcceptablePermissionClaim { + // Build a map of current claims by key + currentMap := make(map[string]apisv1alpha2.AcceptablePermissionClaim) + for _, claim := range currentClaims { + key := claimKey(claim.Group, claim.Resource, claim.IdentityHash) + currentMap[key] = claim + } + + // Reject all export claims + newClaims := make([]apisv1alpha2.AcceptablePermissionClaim, 0, len(exportClaims)) + for _, exportClaim := range exportClaims { + key := claimKey(exportClaim.Group, exportClaim.Resource, exportClaim.IdentityHash) + if existing, ok := currentMap[key]; ok { + // Update state to Rejected if not already + existing.State = apisv1alpha2.ClaimRejected + newClaims = append(newClaims, existing) + } else { + // Create new rejected claim + newClaims = append(newClaims, apisv1alpha2.AcceptablePermissionClaim{ + ScopedPermissionClaim: apisv1alpha2.ScopedPermissionClaim{ + PermissionClaim: exportClaim, + Selector: apisv1alpha2.PermissionClaimSelector{ + MatchAll: true, + }, + }, + State: apisv1alpha2.ClaimRejected, + }) + } + } + + return newClaims +} + +func rejectClaim(exportClaims []apisv1alpha2.PermissionClaim, currentClaims []apisv1alpha2.AcceptablePermissionClaim, gr schema.GroupResource, identityHash string) ([]apisv1alpha2.AcceptablePermissionClaim, bool) { + // Find the export claim + var targetClaim *apisv1alpha2.PermissionClaim + for i := range exportClaims { + claim := &exportClaims[i] + if claim.Resource == gr.Resource && claim.Group == gr.Group { + if identityHash == "" || claim.IdentityHash == identityHash { + targetClaim = claim + break + } + } + } + + if targetClaim == nil { + return nil, false + } + + // Build new claims list + key := claimKey(targetClaim.Group, targetClaim.Resource, targetClaim.IdentityHash) + found := false + newClaims := make([]apisv1alpha2.AcceptablePermissionClaim, 0, len(currentClaims)+1) + + for _, claim := range currentClaims { + claimK := claimKey(claim.Group, claim.Resource, claim.IdentityHash) + if claimK == key { + // Update existing claim to Rejected + claim.State = apisv1alpha2.ClaimRejected + found = true + } + newClaims = append(newClaims, claim) + } + + if !found { + // Add new rejected claim + newClaims = append(newClaims, apisv1alpha2.AcceptablePermissionClaim{ + ScopedPermissionClaim: apisv1alpha2.ScopedPermissionClaim{ + PermissionClaim: *targetClaim, + Selector: apisv1alpha2.PermissionClaimSelector{ + MatchAll: true, + }, + }, + State: apisv1alpha2.ClaimRejected, + }) + } + + return newClaims, true +} diff --git a/staging/src/github.com/kcp-dev/cli/pkg/helpers/apis/apis/apibinding.go b/staging/src/github.com/kcp-dev/cli/pkg/helpers/apis/apis/apibinding.go index 5d80eba655f..4ac8dc8278d 100644 --- a/staging/src/github.com/kcp-dev/cli/pkg/helpers/apis/apis/apibinding.go +++ b/staging/src/github.com/kcp-dev/cli/pkg/helpers/apis/apis/apibinding.go @@ -30,7 +30,10 @@ type APIBinding interface { Name() string Refresh(ctx context.Context, client kcpclientset.Interface) error Create(ctx context.Context, client kcpclientset.Interface) error + Update(ctx context.Context, client kcpclientset.Interface) error SetPermissionClaims(claims []apisv1alpha2.AcceptablePermissionClaim) error + GetPermissionClaims() []apisv1alpha2.AcceptablePermissionClaim + GetExportPermissionClaims() []apisv1alpha2.PermissionClaim IsBound() bool } diff --git a/staging/src/github.com/kcp-dev/cli/pkg/helpers/apis/apis/apibinding_v1alpha1.go b/staging/src/github.com/kcp-dev/cli/pkg/helpers/apis/apis/apibinding_v1alpha1.go index 3af3520f562..6cdcf712453 100644 --- a/staging/src/github.com/kcp-dev/cli/pkg/helpers/apis/apis/apibinding_v1alpha1.go +++ b/staging/src/github.com/kcp-dev/cli/pkg/helpers/apis/apis/apibinding_v1alpha1.go @@ -102,6 +102,58 @@ func (b *apiBindingV1alpha1) SetPermissionClaims(claims []apisv1alpha2.Acceptabl return nil } +func (b *apiBindingV1alpha1) Update(ctx context.Context, client kcpclientset.Interface) error { + updated, err := client.ApisV1alpha1().APIBindings().Update(ctx, b.binding, metav1.UpdateOptions{}) + if err != nil { + return err + } + b.binding = updated + + return nil +} + +func (b *apiBindingV1alpha1) GetPermissionClaims() []apisv1alpha2.AcceptablePermissionClaim { + alpha2Claims := []apisv1alpha2.AcceptablePermissionClaim{} + + for _, claim := range b.binding.Spec.PermissionClaims { + alpha2Claims = append(alpha2Claims, apisv1alpha2.AcceptablePermissionClaim{ + ScopedPermissionClaim: apisv1alpha2.ScopedPermissionClaim{ + PermissionClaim: apisv1alpha2.PermissionClaim{ + GroupResource: apisv1alpha2.GroupResource{ + Group: claim.Group, + Resource: claim.Resource, + }, + IdentityHash: claim.IdentityHash, + Verbs: []string{"*"}, + }, + Selector: apisv1alpha2.PermissionClaimSelector{ + MatchAll: true, + }, + }, + State: apisv1alpha2.AcceptablePermissionClaimState(claim.State), + }) + } + + return alpha2Claims +} + +func (b *apiBindingV1alpha1) GetExportPermissionClaims() []apisv1alpha2.PermissionClaim { + alpha2Claims := []apisv1alpha2.PermissionClaim{} + + for _, claim := range b.binding.Status.ExportPermissionClaims { + alpha2Claims = append(alpha2Claims, apisv1alpha2.PermissionClaim{ + GroupResource: apisv1alpha2.GroupResource{ + Group: claim.Group, + Resource: claim.Resource, + }, + IdentityHash: claim.IdentityHash, + Verbs: []string{"*"}, + }) + } + + return alpha2Claims +} + type apiBindingListV1alpha1 struct { bindings []*apisv1alpha1.APIBinding } diff --git a/staging/src/github.com/kcp-dev/cli/pkg/helpers/apis/apis/apibinding_v1alpha2.go b/staging/src/github.com/kcp-dev/cli/pkg/helpers/apis/apis/apibinding_v1alpha2.go index dd30031460a..f5c1aab4174 100644 --- a/staging/src/github.com/kcp-dev/cli/pkg/helpers/apis/apis/apibinding_v1alpha2.go +++ b/staging/src/github.com/kcp-dev/cli/pkg/helpers/apis/apis/apibinding_v1alpha2.go @@ -70,11 +70,29 @@ func (b *apiBindingV1alpha2) Name() string { return b.binding.Name } +func (b *apiBindingV1alpha2) Update(ctx context.Context, client kcpclientset.Interface) error { + updated, err := client.ApisV1alpha2().APIBindings().Update(ctx, b.binding, metav1.UpdateOptions{}) + if err != nil { + return err + } + b.binding = updated + + return nil +} + func (b *apiBindingV1alpha2) SetPermissionClaims(claims []apisv1alpha2.AcceptablePermissionClaim) error { b.binding.Spec.PermissionClaims = claims return nil } +func (b *apiBindingV1alpha2) GetPermissionClaims() []apisv1alpha2.AcceptablePermissionClaim { + return b.binding.Spec.PermissionClaims +} + +func (b *apiBindingV1alpha2) GetExportPermissionClaims() []apisv1alpha2.PermissionClaim { + return b.binding.Status.ExportPermissionClaims +} + type apiBindingListV1alpha2 struct { bindings []*apisv1alpha2.APIBinding }