Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 40 additions & 85 deletions cli/pkg/kubectl/bind-apiservice/plugin/binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,6 @@
}
}

// TODO: bindFromFile and bindFromResponse can likely share a lot of code. This slow is bit repetitive
// but keeps the two paths separate for clarity. But it needs love.
// https://github.com/kube-bind/kube-bind/issues/360

func (b *Binder) BindFromFile(ctx context.Context) ([]*kubebindv1alpha2.APIServiceBinding, error) {
// Generate the kubectl command that would be equivalent
remoteFlags := ""
Expand All @@ -81,54 +77,19 @@
fmt.Fprintf(b.opts.IOStreams.ErrOut, "✨ Use \"-o yaml\" and \"--dry-run\" to get the APIServiceExportRequest.\n")
fmt.Fprintf(b.opts.IOStreams.ErrOut, " and pass it to \"kubectl bind apiservice\" directly. Great for automation.\n")

// Ensure client side namespace exists
err := b.ensureClientSideNamespaceExists(ctx)
if err != nil {
return nil, fmt.Errorf("failed to ensure kube-bind namespace exists: %w", err)
}

remoteKubeconfig, _, _, err := b.getRemoteKubeconfig(ctx, "", "")
if err != nil {
return nil, err
}

// Copy kubeconfig into local cluster
remoteHost, remoteNamespace, err := base.ParseRemoteKubeconfig([]byte(remoteKubeconfig))
remoteKubeconfigResolved, remoteNamespaceActual, remoteConfig, err := b.setupEnvironment(ctx, []byte(remoteKubeconfig))
if err != nil {
return nil, err
}

kubeClient, err := kubeclient.NewForConfig(b.config)
if err != nil {
return nil, fmt.Errorf("failed to create kube client: %w", err)
}

secretName, err := base.FindRemoteKubeconfig(ctx, kubeClient, remoteNamespace, remoteHost)
if err != nil {
return nil, err
}

secret, created, err := base.EnsureKubeconfigSecret(ctx, remoteKubeconfig, secretName, kubeClient)
if err != nil {
return nil, err
}

if created {
fmt.Fprintf(b.opts.IOStreams.ErrOut, "🔒 Created secret %s/%s for host %s, namespace %s\n", "kube-bind", secret.Name, remoteHost, remoteNamespace)
} else {
fmt.Fprintf(b.opts.IOStreams.ErrOut, "🔒 Updated secret %s/%s for host %s, namespace %s\n", "kube-bind", secret.Name, remoteHost, remoteNamespace)
}

if b.opts.DryRun {
return nil, nil
}

// Get remote kubeconfig
remoteKubeconfig, remoteNamespaceActual, remoteConfig, err := b.getRemoteKubeconfig(ctx, secret.Namespace, secret.Name)
if err != nil {
return nil, fmt.Errorf("failed to get remote kubeconfig: %w", err)
}

data, err := b.getRequestManifest()
if err != nil {
return nil, fmt.Errorf("failed to get request manifest: %w", err)
Expand All @@ -139,61 +100,68 @@
return nil, fmt.Errorf("failed to unmarshal request manifest: %w", err)
}

// Deploy konnector if needed
if err := b.deployKonnector(ctx); err != nil {
return nil, fmt.Errorf("failed to deploy konnector: %w", err)
return b.processRequests(ctx, []*kubebindv1alpha2.APIServiceExportRequest{request}, remoteConfig, remoteNamespaceActual, remoteKubeconfigResolved)
}

Check failure on line 104 in cli/pkg/kubectl/bind-apiservice/plugin/binder.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (gci)
// BindFromResponse processes a BindingResourceResponse and creates all necessary bindings
func (b *Binder) BindFromResponse(ctx context.Context, response *kubebindv1alpha2.BindingResourceResponse) ([]*kubebindv1alpha2.APIServiceBinding, error) {
if response == nil || response.Authentication.OAuth2CodeGrant == nil {
return nil, fmt.Errorf("unexpected response: authentication.oauth2CodeGrant is nil")
}

// Create bindings for all requests
result, err := b.createServiceExportRequest(ctx, remoteConfig, remoteNamespaceActual, request)
remoteKubeconfigResolved, remoteNamespaceActual, remoteConfig, err := b.setupEnvironment(ctx, response.Kubeconfig)
if err != nil {
return nil, fmt.Errorf("failed to create service export request: %w", err)
return nil, err
}

secretName, err = b.createKubeconfigSecret(ctx, remoteConfig.Host, remoteNamespaceActual, remoteKubeconfig)
if err != nil {
return nil, fmt.Errorf("failed to create kubeconfig secret: %w", err)
if b.opts.DryRun {
return nil, nil
}

results, err := b.createAPIServiceBindings(ctx, result, secretName)
if err != nil {
return nil, fmt.Errorf("failed to create API service bindings: %w", err)
// Extract the requests
apiRequests := make([]*kubebindv1alpha2.APIServiceExportRequest, len(response.Requests))
for i, request := range response.Requests {
var meta metav1.TypeMeta
if err := json.Unmarshal(request.Raw, &meta); err != nil {
return nil, fmt.Errorf("unexpected response: failed to unmarshal request #%d: %v", i, err)
}
if got, expected := meta.APIVersion, kubebindv1alpha2.SchemeGroupVersion.String(); got != expected {
return nil, fmt.Errorf("unexpected response: request #%d is not %s, got %s", i, expected, got)
}
var apiRequest kubebindv1alpha2.APIServiceExportRequest
if err := json.Unmarshal(request.Raw, &apiRequest); err != nil {
return nil, fmt.Errorf("failed to unmarshal api request #%d: %v", i+1, err)
}
apiRequests[i] = &apiRequest
}

return results, nil
return b.processRequests(ctx, apiRequests, remoteConfig, remoteNamespaceActual, remoteKubeconfigResolved)
}

// BindFromResponse processes a BindingResourceResponse and creates all necessary bindings
func (b *Binder) BindFromResponse(ctx context.Context, response *kubebindv1alpha2.BindingResourceResponse) ([]*kubebindv1alpha2.APIServiceBinding, error) {
if response == nil || response.Authentication.OAuth2CodeGrant == nil {
return nil, fmt.Errorf("unexpected response: authentication.oauth2CodeGrant is nil")
}

func (b *Binder) setupEnvironment(ctx context.Context, remoteKubeconfigBytes []byte) (string, string, *rest.Config, error) {
// Ensure client side namespace exists
err := b.ensureClientSideNamespaceExists(ctx)
if err != nil {
return nil, fmt.Errorf("failed to ensure kube-bind namespace exists: %w", err)
return "", "", nil, fmt.Errorf("failed to ensure kube-bind namespace exists: %w", err)
}

// Copy kubeconfig into local cluster
remoteHost, remoteNamespace, err := base.ParseRemoteKubeconfig(response.Kubeconfig)
remoteHost, remoteNamespace, err := base.ParseRemoteKubeconfig(remoteKubeconfigBytes)
if err != nil {
return nil, err
return "", "", nil, err
}

kubeClient, err := kubeclient.NewForConfig(b.config)
if err != nil {
return nil, fmt.Errorf("failed to create kube client: %w", err)
return "", "", nil, fmt.Errorf("failed to create kube client: %w", err)
}

secretName, err := base.FindRemoteKubeconfig(ctx, kubeClient, remoteNamespace, remoteHost)
if err != nil {
return nil, err
return "", "", nil, err
}

secret, created, err := base.EnsureKubeconfigSecret(ctx, string(response.Kubeconfig), secretName, kubeClient)
secret, created, err := base.EnsureKubeconfigSecret(ctx, string(remoteKubeconfigBytes), secretName, kubeClient)
if err != nil {
return nil, err
return "", "", nil, err
}

if created {
Expand All @@ -203,32 +171,19 @@
}

if b.opts.DryRun {
return nil, nil
return "", "", nil, nil
}

// Get remote kubeconfig
remoteKubeconfig, remoteNamespaceActual, remoteConfig, err := b.getRemoteKubeconfig(ctx, secret.Namespace, secret.Name)
if err != nil {
return nil, fmt.Errorf("failed to get remote kubeconfig: %w", err)
return "", "", nil, fmt.Errorf("failed to get remote kubeconfig: %w", err)
}

// Extract the requests
apiRequests := make([]*kubebindv1alpha2.APIServiceExportRequest, len(response.Requests))
for i, request := range response.Requests {
var meta metav1.TypeMeta
if err := json.Unmarshal(request.Raw, &meta); err != nil {
return nil, fmt.Errorf("unexpected response: failed to unmarshal request #%d: %v", i, err)
}
if got, expected := meta.APIVersion, kubebindv1alpha2.SchemeGroupVersion.String(); got != expected {
return nil, fmt.Errorf("unexpected response: request #%d is not %s, got %s", i, expected, got)
}
var apiRequest kubebindv1alpha2.APIServiceExportRequest
if err := json.Unmarshal(request.Raw, &apiRequest); err != nil {
return nil, fmt.Errorf("failed to unmarshal api request #%d: %v", i+1, err)
}
apiRequests[i] = &apiRequest
}
return remoteKubeconfig, remoteNamespaceActual, remoteConfig, nil
}

func (b *Binder) processRequests(ctx context.Context, apiRequests []*kubebindv1alpha2.APIServiceExportRequest, remoteConfig *rest.Config, remoteNamespaceActual string, remoteKubeconfig string) ([]*kubebindv1alpha2.APIServiceBinding, error) {
// Deploy konnector if needed
if err := b.deployKonnector(ctx); err != nil {
return nil, fmt.Errorf("failed to deploy konnector: %w", err)
Expand Down
Loading