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
45 changes: 45 additions & 0 deletions plugins/ibmcloud/api_key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ibmcloud

import (
"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/credname"
"github.com/1Password/shell-plugins/sdk/schema/fieldname"
)

func APIKey() schema.CredentialType {
return schema.CredentialType{
Name: credname.APIKey,
DocsURL: sdk.URL("https://cloud.ibm.com/docs/account?topic=account-manapikey"),
ManagementURL: sdk.URL("https://cloud.ibm.com/iam/overview"),
Fields: []schema.CredentialField{
{
Name: fieldname.APIKey,
MarkdownDescription: "API Key used to authenticate to IBM Cloud.",
Secret: true,
Composition: &schema.ValueComposition{
Length: 44,
Charset: schema.Charset{
Uppercase: true,
Lowercase: true,
Digits: true,
},
},
},
{
Name: sdk.FieldName("resource group"),
MarkdownDescription: "Resource group to target when logging in to IBM Cloud.",
Optional: true,
},
},
DefaultProvisioner: provision.EnvVars(defaultEnvVarMapping),
Importer: importer.TryAll(
importer.TryEnvVarPair(defaultEnvVarMapping),
)}
}

var defaultEnvVarMapping = map[string]sdk.FieldName{
"IBMCLOUD_API_KEY": fieldname.APIKey,
}
45 changes: 45 additions & 0 deletions plugins/ibmcloud/api_key_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ibmcloud

import (
"testing"

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

func TestAPIKeyProvisioner(t *testing.T) {
plugintest.TestProvisioner(t, APIKey().DefaultProvisioner, map[string]plugintest.ProvisionCase{
"default": {
ItemFields: map[sdk.FieldName]string{
fieldname.APIKey: "GeYS3RmGXo7cQhY8UboUSLmWarFF1HGqv4fVKEXAMPLE",
},
ExpectedOutput: sdk.ProvisionOutput{
Environment: map[string]string{
"IBMCLOUD_API_KEY": "GeYS3RmGXo7cQhY8UboUSLmWarFF1HGqv4fVKEXAMPLE",
},
},
},
})
}

func TestAPIKeyImporter(t *testing.T) {
plugintest.TestImporter(t, APIKey().Importer, map[string]plugintest.ImportCase{
"environment": {
Environment: map[string]string{
"IBMCLOUD_API_KEY": "GeYS3RmGXo7cQhY8UboUSLmWarFF1HGqv4fVKEXAMPLE",
},
ExpectedCandidates: []sdk.ImportCandidate{
{
Fields: map[sdk.FieldName]string{
fieldname.APIKey: "GeYS3RmGXo7cQhY8UboUSLmWarFF1HGqv4fVKEXAMPLE",
},
},
},
},
"config file": {
Files: map[string]string{},
ExpectedCandidates: []sdk.ImportCandidate{},
},
})
}
70 changes: 70 additions & 0 deletions plugins/ibmcloud/cli_provisioner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package ibmcloud

import (
"context"
"strings"

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

// CLIProvisioner is responsible for provisioning the IBM Cloud CLI with the appropriate credentials
type CLIProvisioner struct {
}

func (p CLIProvisioner) Provision(ctx context.Context, in sdk.ProvisionInput, out *sdk.ProvisionOutput) {
// First, set the API key as an environment variable
out.AddEnvVar("IBMCLOUD_API_KEY", in.ItemFields[fieldname.APIKey])

// Check if we need to add a resource group flag to the login command
targetGroup := ""
found := false
// Try exact match first
if tg, ok := in.ItemFields[sdk.FieldName("resource group")]; ok && tg != "" {
targetGroup = tg
found = true
}

// Try with trimmed whitespace
if !found {
for fieldName, fieldValue := range in.ItemFields {
fieldNameStr := string(fieldName)
trimmedName := strings.TrimSpace(fieldNameStr)
if trimmedName == "resource group" && fieldValue != "" {
targetGroup = fieldValue
found = true
break
}
}
}

if found && targetGroup != "" {
// Only modify the command line if it's an ibmcloud login command
if len(out.CommandLine) > 1 && out.CommandLine[1] == "login" {
// Check if the command already has a -g or --target-group flag
hasTargetGroupFlag := false
for _, arg := range out.CommandLine {
if arg == "-g" || arg == "--target-group" || strings.HasPrefix(arg, "-g=") || strings.HasPrefix(arg, "--target-group=") {
hasTargetGroupFlag = true
break
}
}

// If no resource group flag is present, add it
if !hasTargetGroupFlag {
newCommandLine := append([]string{}, out.CommandLine[0], out.CommandLine[1])
newCommandLine = append(newCommandLine, "-g", targetGroup)
newCommandLine = append(newCommandLine, out.CommandLine[2:]...)
out.CommandLine = newCommandLine
}
}
}
}

func (p CLIProvisioner) Deprovision(ctx context.Context, in sdk.DeprovisionInput, out *sdk.DeprovisionOutput) {
// Nothing to do here: environment variables get wiped automatically when the process exits.
}

func (p CLIProvisioner) Description() string {
return "Provision environment variables with IBM Cloud API Key and add resource group flag to login command if specified"
}
69 changes: 69 additions & 0 deletions plugins/ibmcloud/cli_provisioner_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package ibmcloud

import (
"testing"

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

func TestCLIProvisioner(t *testing.T) {
provisioner := CLIProvisioner{}

testCases := map[string]plugintest.ProvisionCase{
"with api key only": {
ItemFields: map[sdk.FieldName]string{
fieldname.APIKey: "GeYS3RmGXo7cQhY8UboUSLmWarFF1HGqv4fVKEXAMPLE",
},
CommandLine: []string{"ibmcloud", "login"},
ExpectedOutput: sdk.ProvisionOutput{
Environment: map[string]string{
"IBMCLOUD_API_KEY": "GeYS3RmGXo7cQhY8UboUSLmWarFF1HGqv4fVKEXAMPLE",
},
CommandLine: []string{"ibmcloud", "login"},
},
},
"with api key and resource group": {
ItemFields: map[sdk.FieldName]string{
fieldname.APIKey: "GeYS3RmGXo7cQhY8UboUSLmWarFF1HGqv4fVKEXAMPLE",
sdk.FieldName("resource group"): "my-resource-group",
},
CommandLine: []string{"ibmcloud", "login"},
ExpectedOutput: sdk.ProvisionOutput{
Environment: map[string]string{
"IBMCLOUD_API_KEY": "GeYS3RmGXo7cQhY8UboUSLmWarFF1HGqv4fVKEXAMPLE",
},
CommandLine: []string{"ibmcloud", "login", "-g", "my-resource-group"},
},
},
"with resource group but not login command": {
ItemFields: map[sdk.FieldName]string{
fieldname.APIKey: "GeYS3RmGXo7cQhY8UboUSLmWarFF1HGqv4fVKEXAMPLE",
sdk.FieldName("resource group"): "my-resource-group",
},
CommandLine: []string{"target"},
ExpectedOutput: sdk.ProvisionOutput{
Environment: map[string]string{
"IBMCLOUD_API_KEY": "GeYS3RmGXo7cQhY8UboUSLmWarFF1HGqv4fVKEXAMPLE",
},
CommandLine: []string{"target"},
},
},
"with resource group and existing group flag": {
ItemFields: map[sdk.FieldName]string{
fieldname.APIKey: "GeYS3RmGXo7cQhY8UboUSLmWarFF1HGqv4fVKEXAMPLE",
sdk.FieldName("resource group"): "my-resource-group",
},
CommandLine: []string{"login", "-g", "existing-group"},
ExpectedOutput: sdk.ProvisionOutput{
Environment: map[string]string{
"IBMCLOUD_API_KEY": "GeYS3RmGXo7cQhY8UboUSLmWarFF1HGqv4fVKEXAMPLE",
},
CommandLine: []string{"login", "-g", "existing-group"},
},
},
}

plugintest.TestProvisioner(t, provisioner, testCases)
}
26 changes: 26 additions & 0 deletions plugins/ibmcloud/ibmcloud.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ibmcloud

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 IBMCloudCLI() schema.Executable {
return schema.Executable{
Name: "IBM Cloud CLI",
Runs: []string{"ibmcloud"},
DocsURL: sdk.URL("https://ibmcloud.com/docs/cli"),
NeedsAuth: needsauth.IfAll(
needsauth.NotForHelpOrVersion(),
needsauth.NotWithoutArgs(),
),
Uses: []schema.CredentialUsage{
{
Name: credname.APIKey,
Provisioner: CLIProvisioner{},
},
},
}
}
22 changes: 22 additions & 0 deletions plugins/ibmcloud/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ibmcloud

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

func New() schema.Plugin {
return schema.Plugin{
Name: "ibmcloud",
Platform: schema.PlatformInfo{
Name: "IBM Cloud",
Homepage: sdk.URL("https://cloud.ibm.com/"),
},
Credentials: []schema.CredentialType{
APIKey(),
},
Executables: []schema.Executable{
IBMCloudCLI(),
},
}
}