Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
24 changes: 24 additions & 0 deletions plugins/helm/helm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package helm

import (
"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/needsauth"
"github.com/1Password/shell-plugins/sdk/schema"
"github.com/1Password/shell-plugins/sdk/schema/credname"
)

func HelmCLI() schema.Executable {
return schema.Executable{
Name: "Helm",
Runs: []string{"helm"},
DocsURL: sdk.URL("https://helm.sh/docs/"),
NeedsAuth: needsauth.IfAll(
needsauth.NotForHelpOrVersion(),
needsauth.NotWithoutArgs(),
),
Uses: []schema.CredentialUsage{
{Name: sdk.CredentialName("Kubeconfig")},
{Name: credname.SecretKey, Plugin: "sops", Optional: true},
},
}
}
41 changes: 41 additions & 0 deletions plugins/helm/helm_credentials.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package helm

import (
"context"
"encoding/base64"

"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/importer"
"github.com/1Password/shell-plugins/sdk/schema"
"github.com/1Password/shell-plugins/sdk/schema/fieldname"
)

func Kubeconfig() schema.CredentialType {
return schema.CredentialType{
Name: sdk.CredentialName("Kubeconfig"),
DocsURL: sdk.URL("https://helm.sh/docs/"),
Fields: []schema.CredentialField{
{
Name: fieldname.Credential,
MarkdownDescription: "Base64-encoded kubeconfig YAML file contents.",
Secret: true,
},
},
DefaultProvisioner: &helmKubeconfigProvisioner{},
Importer: importer.TryAll(
TryKubeconfigFile(),
),
}
}

func TryKubeconfigFile() sdk.Importer {
return importer.TryFile("~/.kube/config", func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) {
encoded := base64.StdEncoding.EncodeToString(contents)
out.AddCandidate(sdk.ImportCandidate{
Fields: map[sdk.FieldName]string{
fieldname.Credential: encoded,
},
NameHint: importer.SanitizeNameHint("default"),
})
})
}
49 changes: 49 additions & 0 deletions plugins/helm/helm_credentials_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package helm

import (
"encoding/base64"
"testing"

"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/plugintest"
"github.com/1Password/shell-plugins/sdk/schema/fieldname"
)

func TestKubeconfigProvisioner(t *testing.T) {
rawConfig := plugintest.LoadFixture(t, "config")
encodedConfig := base64.StdEncoding.EncodeToString([]byte(rawConfig))

plugintest.TestProvisioner(t, Kubeconfig().DefaultProvisioner, map[string]plugintest.ProvisionCase{
"kubeconfig": {
ItemFields: map[sdk.FieldName]string{
fieldname.Credential: encodedConfig,
},
ExpectedOutput: sdk.ProvisionOutput{
Environment: map[string]string{
"KUBECONFIG": "/tmp/config",
},
},
},
})
}

func TestKubeconfigImporter(t *testing.T) {
rawConfig := plugintest.LoadFixture(t, "config")
encodedConfig := base64.StdEncoding.EncodeToString([]byte(rawConfig))

plugintest.TestImporter(t, Kubeconfig().Importer, map[string]plugintest.ImportCase{
"kubeconfig file": {
Files: map[string]string{
"~/.kube/config": rawConfig,
},
ExpectedCandidates: []sdk.ImportCandidate{
{
Fields: map[sdk.FieldName]string{
fieldname.Credential: encodedConfig,
},
NameHint: "",
},
},
},
})
}
24 changes: 24 additions & 0 deletions plugins/helm/helmfile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package helm

import (
"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/needsauth"
"github.com/1Password/shell-plugins/sdk/schema"
"github.com/1Password/shell-plugins/sdk/schema/credname"
)

func HelmfileCLI() schema.Executable {
return schema.Executable{
Name: "Helmfile",
Runs: []string{"helmfile"},
DocsURL: sdk.URL("https://github.com/helmfile/helmfile"),
NeedsAuth: needsauth.IfAll(
needsauth.NotForHelpOrVersion(),
needsauth.NotWithoutArgs(),
),
Uses: []schema.CredentialUsage{
{Name: sdk.CredentialName("Kubeconfig")},
{Name: credname.SecretKey, Plugin: "sops", Optional: true},
},
}
}
23 changes: 23 additions & 0 deletions plugins/helm/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package helm

import (
"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/schema"
)

func New() schema.Plugin {
return schema.Plugin{
Name: "helm",
Platform: schema.PlatformInfo{
Name: "Helm",
Homepage: sdk.URL("https://helm.sh"),
},
Credentials: []schema.CredentialType{
Kubeconfig(),
},
Executables: []schema.Executable{
HelmCLI(),
HelmfileCLI(),
},
}
}
42 changes: 42 additions & 0 deletions plugins/helm/provisioner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package helm

import (
"context"
"encoding/base64"
"os"
"path/filepath"

"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/schema/fieldname"
)

type helmKubeconfigProvisioner struct{}

func (p *helmKubeconfigProvisioner) Description() string {
return "Provision kubeconfig file for Helm"
}

func (p *helmKubeconfigProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) {
// Decode base64 kubeconfig
encoded := in.ItemFields[fieldname.Credential]
decoded, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
out.AddError(err)
return
}

// Write kubeconfig as a real file (not via out.AddSecretFile which creates a FIFO).
// Helm reads the kubeconfig multiple times, and FIFOs block on the second read.
configPath := filepath.Join(in.TempDir, "config")
if err := os.WriteFile(configPath, decoded, 0600); err != nil {
out.AddError(err)
return
}
out.AddEnvVar("KUBECONFIG", configPath)
}

func (p *helmKubeconfigProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) {
// Remove kubeconfig written directly to disk
configPath := filepath.Join(in.TempDir, "config")
os.Remove(configPath)
}
19 changes: 19 additions & 0 deletions plugins/helm/test-fixtures/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJlRENDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdGMyVnkKZG1WeUxXTmhRREUzTURBeU16TTJOREV3SGhjTk1qTXhNVEU1TVRBeU56SXhXaGNOTXpNeE1URTJNVEF5TnpJeApXakFqTVNFd0h3WURWUVFEREJock0zTXRjMlZ5ZG1WeUxXTmhRREUzTURBeU16TTJOREV3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFUMUZiY29ycjhBQ1NkcmNqQk5xQ3JSa0M2Y2tMYmZUQ0NSSUN2UndhN2IKUjZiNUxRZHZuOXZVYkhiVHN4Z3FJQ2pYYjZRTEtIRTRrQXlQQ3BDNnBPUEpvMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVWVlUllndjZBbGFCNVc0dDV0Rm9ZCm9OSHVkVDB3Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQUk0TlQyR3hxNHYwZExHRlFkNkdtbHdaZ0dBckVZRWoKL1FkeWZsOU12QTZyQWlFQWljVjdiVzlqcVJwNjh4cHdJeDlYak9OeUt2V3dZMVdQVWYzQU1kQTd6T2s9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
server: https://10.0.0.1:6443
name: default
contexts:
- context:
cluster: default
user: default
name: default
current-context: default
preferences: {}
users:
- name: default
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SUJBZ0lJVHVNdTNtNE8waTh3Q2dZSUtvWkl6ajBFQXdJd0l6RWhNQjhHQTFVRUF3d1kKYXpOekxXTnNhV1Z1ZEMxallVQXhOekF3TWpNek5qUXhNQjRYRFRJek1URXhPVEV3TWpjeU1Wb1hEVEkwTVRFeApPREV3TWpjeU1Wb3dNREVYTUJVR0ExVUVDaE1PYzNsemRHVnRPbTFoYzNSbGNuTXhGVEFUQmdOVkJBTVRESE41CmMzUmxiVHBoWkcxcGJqQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJNL0hvRHZUZmg2dEtoVVUKM3lHZHBrU05EWVhGWjBYYjVVMHRKRzh4TlFiQmEzaDhRQjJMMVN0MDVNRlQ2dEhxUWpsUkJ6cG1qRnJ2L1haQQp1RUl4WmRlalNEQkdNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUtCZ2dyQmdFRkJRY0RBakFmCkJnTlZIU01FR0RBV2dCUmYwYnh0NTVSdlE4M3pxMGxrdnRXU28zYUNDREFLQmdncWhrak9QUVFEQWdOSUFEQkYKQWlFQXo1K2txTWJpMjJKMW1LNGFldlVYUVFGVm9hSXFWRFBzTjZHcW1iZGxaRWtDSUJqeWVxc0tkL0VxdFBXbgpaSXRCL21STC9WdDRHRUhJbGpUckxLNytlN2ZOCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJkekNDQVIyZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQWpNU0V3SHdZRFZRUUREQmhyTTNNdFkyeHAKWlc1MExXTmhRREUzTURBeU16TTJOREV3SGhjTk1qTXhNVEU1TVRBeU56SXhXaGNOTXpNeE1URTJNVEF5TnpJeApXakFqTVNFd0h3WURWUVFEREJock0zTXRZMnhwWlc1MExXTmhRREUzTURBeU16TTJOREV3V1RBVEJnY3Foa2pPClBRSUJCZ2dxaGtqT1BRTUJCd05DQUFSMU9ucCsxUnpLYVVVN0lJNTVhTXZJRUFXeE9udmNqd0E0NnRZSDQ3amkKb0dIVFViTHJXc3ZxMHFZeEpJZlkzRmdZekZYMVlkLzhBbVJxWGJrTldkUW9vMEl3UURBT0JnTlZIUThCQWY4RQpCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVVg5RzhiZWVVYjBQTjg2dEpaTC9WCmtxTjJnZ2d3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUxoSER3UlllVXBoS0ZSOURKZ2EycjlLRlBmQ2dLSnUKd0JOK2ptOW4wdm1YQWlBenM5Q0hDN1F5aElFSnNmVjVLaGF1MHhqQ1pYdEJpblQ5aEt4d0RGMTJGUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
client-key-data: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IUUNBUUVFSUFTbE5FV3lPRWRuSWxYVWpRdG1Pc1cwWjdwYWxpTUlKdENMcmhKSGZRYlBvQWNHQlN1QkJBQUsKb1VRRFFnQUV6OGVnTzlOK0hxMHFGUlRmSVoybVJJME5oY1ZuUmR2bFRTMGtieksxQnNGcmVIeEFIWXZWSzNUawp3VlBxMGVwQ09WRUhPbWFNV3UvOWRrQzRRakZsMXc9PQotLS0tLUVORCBFQyBQUklWQVRFIEtFWS0tLS0tCg==
23 changes: 23 additions & 0 deletions plugins/kubernetes/k9s.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package kubernetes

import (
"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/needsauth"
"github.com/1Password/shell-plugins/sdk/schema"
)

func K9sCLI() schema.Executable {
return schema.Executable{
Name: "k9s",
Runs: []string{"k9s"},
DocsURL: sdk.URL("https://k9scli.io/"),
NeedsAuth: needsauth.IfAll(
needsauth.NotForHelpOrVersion(),
),
Uses: []schema.CredentialUsage{
{
Name: sdk.CredentialName("Kubeconfig"),
},
},
}
}
56 changes: 56 additions & 0 deletions plugins/kubernetes/kubeconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package kubernetes

import (
"context"
"encoding/base64"

"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/importer"
"github.com/1Password/shell-plugins/sdk/provision"
"github.com/1Password/shell-plugins/sdk/schema"
"github.com/1Password/shell-plugins/sdk/schema/fieldname"
)

func Kubeconfig() schema.CredentialType {
return schema.CredentialType{
Name: sdk.CredentialName("Kubeconfig"),
DocsURL: sdk.URL("https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/"),
Fields: []schema.CredentialField{
{
Name: fieldname.Credential,
MarkdownDescription: "Base64-encoded kubeconfig YAML file contents.",
Secret: true,
},
},
DefaultProvisioner: provision.TempFile(
kubeconfigFileContents,
provision.Filename("config"),
provision.SetPathAsEnvVar("KUBECONFIG"),
),
Importer: importer.TryAll(
TryKubeconfigFile(),
),
}
}

func kubeconfigFileContents(in sdk.ProvisionInput) ([]byte, error) {
encoded := in.ItemFields[fieldname.Credential]
decoded, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
return nil, err
}
return decoded, nil
}

func TryKubeconfigFile() sdk.Importer {
return importer.TryFile("~/.kube/config", func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) {
// Import existing kubeconfig as base64-encoded
encoded := base64.StdEncoding.EncodeToString(contents)
out.AddCandidate(sdk.ImportCandidate{
Fields: map[sdk.FieldName]string{
fieldname.Credential: encoded,
},
NameHint: importer.SanitizeNameHint("default"),
})
})
}
54 changes: 54 additions & 0 deletions plugins/kubernetes/kubeconfig_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package kubernetes

import (
"encoding/base64"
"testing"

"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/plugintest"
"github.com/1Password/shell-plugins/sdk/schema/fieldname"
)

func TestKubeconfigProvisioner(t *testing.T) {
rawConfig := plugintest.LoadFixture(t, "config")
encodedConfig := base64.StdEncoding.EncodeToString([]byte(rawConfig))

plugintest.TestProvisioner(t, Kubeconfig().DefaultProvisioner, map[string]plugintest.ProvisionCase{
"default": {
ItemFields: map[sdk.FieldName]string{
fieldname.Credential: encodedConfig,
},
ExpectedOutput: sdk.ProvisionOutput{
Environment: map[string]string{
"KUBECONFIG": "/tmp/config",
},
Files: map[string]sdk.OutputFile{
"/tmp/config": {
Contents: []byte(rawConfig),
},
},
},
},
})
}

func TestKubeconfigImporter(t *testing.T) {
rawConfig := plugintest.LoadFixture(t, "config")
encodedConfig := base64.StdEncoding.EncodeToString([]byte(rawConfig))

plugintest.TestImporter(t, Kubeconfig().Importer, map[string]plugintest.ImportCase{
"kubeconfig file": {
Files: map[string]string{
"~/.kube/config": rawConfig,
},
ExpectedCandidates: []sdk.ImportCandidate{
{
Fields: map[sdk.FieldName]string{
fieldname.Credential: encodedConfig,
},
NameHint: "",
},
},
},
})
}
24 changes: 24 additions & 0 deletions plugins/kubernetes/kubectl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package kubernetes

import (
"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/needsauth"
"github.com/1Password/shell-plugins/sdk/schema"
)

func KubectlCLI() schema.Executable {
return schema.Executable{
Name: "kubectl",
Runs: []string{"kubectl"},
DocsURL: sdk.URL("https://kubernetes.io/docs/reference/kubectl/"),
NeedsAuth: needsauth.IfAll(
needsauth.NotForHelpOrVersion(),
needsauth.NotWithoutArgs(),
),
Uses: []schema.CredentialUsage{
{
Name: sdk.CredentialName("Kubeconfig"),
},
},
}
}
Loading