Skip to content
2 changes: 2 additions & 0 deletions cmd/mapt/cmd/ibmcloud/hosts/ibm-power.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,14 @@ func ibmPowerDestroy() *cobra.Command {
DebugLevel: viper.GetUint(params.DebugLevel),
Serverless: viper.IsSet(params.Serverless),
ForceDestroy: viper.IsSet(params.ForceDestroy),
KeepState: viper.IsSet(params.KeepState),
})
},
}
flagSet := pflag.NewFlagSet(params.DestroyCmdName, pflag.ExitOnError)
flagSet.Bool(params.Serverless, false, params.ServerlessDesc)
flagSet.Bool(params.ForceDestroy, false, params.ForceDestroyDesc)
flagSet.Bool(params.KeepState, false, params.KeepStateDesc)
c.PersistentFlags().AddFlagSet(flagSet)
return c
}
2 changes: 2 additions & 0 deletions cmd/mapt/cmd/ibmcloud/hosts/ibm-z.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,14 @@ func ibmZDestroy() *cobra.Command {
DebugLevel: viper.GetUint(params.DebugLevel),
Serverless: viper.IsSet(params.Serverless),
ForceDestroy: viper.IsSet(params.ForceDestroy),
KeepState: viper.IsSet(params.KeepState),
})
},
}
flagSet := pflag.NewFlagSet(params.DestroyCmdName, pflag.ExitOnError)
flagSet.Bool(params.Serverless, false, params.ServerlessDesc)
flagSet.Bool(params.ForceDestroy, false, params.ForceDestroyDesc)
flagSet.Bool(params.KeepState, false, params.KeepStateDesc)
c.PersistentFlags().AddFlagSet(flagSet)
return c
}
47 changes: 47 additions & 0 deletions docs/ibmcloud/ibm-power.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ On first boot, cloud-init automatically configures the PowerVS instance for on-p
| `IBMCLOUD_ACCOUNT` | yes | IBM Cloud account ID |
| `IBMCLOUD_API_KEY` | yes | IBM Cloud API key |
| `IC_REGION` | yes | IBM Cloud region (e.g. `us-south`, `us-east`) |
| `IBMCLOUD_COS_ACCESS_KEY_ID` | only with S3 `--backed-url` | HMAC access key for IBM Cloud Object Storage |
| `IBMCLOUD_COS_SECRET_ACCESS_KEY` | only with S3 `--backed-url` | HMAC secret key for IBM Cloud Object Storage |
| `IBMCLOUD_COS_ENDPOINT` | no | COS S3 endpoint (defaults to `s3.<region>.cloud-object-storage.appdomain.cloud`) |

## Create

Expand Down Expand Up @@ -134,6 +137,34 @@ podman run -d --name ibm-power \
--otel-auth-token <uuid-token>
```

## Using IBM Cloud Object Storage as S3 backend

To store Pulumi state in IBM COS instead of a local file, create [HMAC credentials](https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-uhc-hmac-credentials-main) for your COS instance and pass an `s3://` backed URL:

```bash
podman run -d --name ibm-power \
-v ${PWD}:/workspace:z \
-e IBMCLOUD_API_KEY=XXX \
-e IBMCLOUD_ACCOUNT=XXX \
-e IC_REGION=us-south \
-e IBMCLOUD_COS_ACCESS_KEY_ID=XXX \
-e IBMCLOUD_COS_SECRET_ACCESS_KEY=XXX \
quay.io/redhat-developer/mapt:v0.8.0 ibmcloud ibm-power create \
--project-name ibm-power \
--backed-url s3://my-cos-bucket \
--conn-details-output /workspace \
--workspace-id <workspace-id> \
--pi-private-subnet-id <private-subnet-id>
```

An HTTPS endpoint URL is also supported as `--backed-url`, with the bucket name in the path:

```
--backed-url https://s3.us-south.cloud-object-storage.appdomain.cloud/my-cos-bucket
```

The COS endpoint and `PULUMI_BACKEND_URL` are constructed automatically from the region and bucket name.

## Destroy

```bash
Expand All @@ -144,4 +175,20 @@ podman run -d --name ibm-power \
quay.io/redhat-developer/mapt:v0.8.0 ibmcloud ibm-power destroy \
--project-name ibm-power \
--backed-url file:///workspace
```

By default, destroy removes the Pulumi state files from the backend after a successful destroy. Use `--keep-state` to preserve them:

```bash
podman run -d --name ibm-power \
-v ${PWD}:/workspace:z \
-e IBMCLOUD_API_KEY=XXX \
-e IBMCLOUD_ACCOUNT=XXX \
-e IC_REGION=us-south \
-e IBMCLOUD_COS_ACCESS_KEY_ID=XXX \
-e IBMCLOUD_COS_SECRET_ACCESS_KEY=XXX \
quay.io/redhat-developer/mapt:v0.8.0 ibmcloud ibm-power destroy \
--project-name ibm-power \
--backed-url s3://my-cos-bucket \
--keep-state
```
46 changes: 46 additions & 0 deletions docs/ibmcloud/ibm-z.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ Two networking modes are supported:
| `IBMCLOUD_API_KEY` | yes | IBM Cloud API key |
| `IC_REGION` | yes | IBM Cloud region (e.g. `us-south`, `us-east`) |
| `IC_ZONE` | only without `--subnet-id` | Availability zone (e.g. `us-south-2`) |
| `IBMCLOUD_COS_ACCESS_KEY_ID` | only with S3 `--backed-url` | HMAC access key for IBM Cloud Object Storage |
| `IBMCLOUD_COS_SECRET_ACCESS_KEY` | only with S3 `--backed-url` | HMAC secret key for IBM Cloud Object Storage |
| `IBMCLOUD_COS_ENDPOINT` | no | COS S3 endpoint (defaults to `s3.<region>.cloud-object-storage.appdomain.cloud`) |

## Create

Expand Down Expand Up @@ -112,6 +115,33 @@ podman run -d --name ibm-z \
--otel-auth-token <uuid-token>
```

## Using IBM Cloud Object Storage as S3 backend

To store Pulumi state in IBM COS instead of a local file, create [HMAC credentials](https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-uhc-hmac-credentials-main) for your COS instance and pass an `s3://` backed URL:

```bash
podman run -d --name ibm-z \
-v ${PWD}:/workspace:z \
-e IBMCLOUD_API_KEY=XXX \
-e IBMCLOUD_ACCOUNT=XXX \
-e IC_REGION=us-south \
-e IC_ZONE=us-south-2 \
-e IBMCLOUD_COS_ACCESS_KEY_ID=XXX \
-e IBMCLOUD_COS_SECRET_ACCESS_KEY=XXX \
quay.io/redhat-developer/mapt:v0.8.0 ibmcloud ibm-z create \
--project-name ibm-z \
--backed-url s3://my-cos-bucket \
--conn-details-output /workspace
```

An HTTPS endpoint URL is also supported as `--backed-url`, with the bucket name in the path:

```
--backed-url https://s3.us-south.cloud-object-storage.appdomain.cloud/my-cos-bucket
```

The COS endpoint and `PULUMI_BACKEND_URL` are constructed automatically from the region and bucket name.

## Destroy

```bash
Expand All @@ -123,3 +153,19 @@ podman run -d --name ibm-z \
--project-name ibm-z \
--backed-url file:///workspace
```

By default, destroy removes the Pulumi state files from the backend after a successful destroy. Use `--keep-state` to preserve them:

```bash
podman run -d --name ibm-z \
-v ${PWD}:/workspace:z \
-e IBMCLOUD_API_KEY=XXX \
-e IBMCLOUD_ACCOUNT=XXX \
-e IC_REGION=us-south \
-e IBMCLOUD_COS_ACCESS_KEY_ID=XXX \
-e IBMCLOUD_COS_SECRET_ACCESS_KEY=XXX \
quay.io/redhat-developer/mapt:v0.8.0 ibmcloud ibm-z destroy \
--project-name ibm-z \
--backed-url s3://my-cos-bucket \
--keep-state
```
8 changes: 6 additions & 2 deletions pkg/manager/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ type Context struct {
}

type Provider interface {
Init(ctx context.Context, backedURL string) error
Init(ctx context.Context, backedURL string) (string, error)
DefaultHostingPlace() (*string, error)
}

Expand Down Expand Up @@ -110,9 +110,13 @@ func Init(ca *ContextArgs, provider Provider) (*Context, error) {
c.targetHostingPlace = *hp
}
// Manage
if err := provider.Init(ctx, ca.BackedURL); err != nil {
resolvedURL, err := provider.Init(ctx, ca.BackedURL)
if err != nil {
return nil, err
}
if resolvedURL != "" {
c.backedURL = resolvedURL
}
// Manage integrations
if err := manageIntegration(c, ca); err != nil {
return nil, err
Expand Down
7 changes: 5 additions & 2 deletions pkg/provider/aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ const pulumiLocksPath = ".pulumi/locks"

type AWS struct{}

func (a *AWS) Init(ctx context.Context, backedURL string) error {
func (a *AWS) Init(ctx context.Context, backedURL string) (string, error) {
// Manage remote state requirements, if backedURL
// is on a different region we need to change to that region
// in order to interact with the state
return manageRemoteState(ctx, backedURL)
return "", manageRemoteState(ctx, backedURL)
}

func (a *AWS) DefaultHostingPlace() (*string, error) {
Expand Down Expand Up @@ -217,6 +217,9 @@ func parseS3BackedURL(mCtx *mc.Context) (*string, *string, error) {
return nil, nil, fmt.Errorf("failed to parse S3 URI: %w", err)
}
key := strings.TrimPrefix(u.Path, "/")
if key == "" {
return nil, nil, fmt.Errorf("invalid S3 URI %q: missing object key after bucket name", mCtx.BackedURL())
}
return &u.Host, &key, nil
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/provider/azure/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ func Provider() *Azure {
return &Azure{}
}

func (a *Azure) Init(ctx context.Context, backedURL string) error {
func (a *Azure) Init(ctx context.Context, backedURL string) (string, error) {
setAZIdentityEnvs()
return nil
return "", nil
}

func (a *Azure) DefaultHostingPlace() (*string, error) {
Expand Down
5 changes: 4 additions & 1 deletion pkg/provider/ibmcloud/action/ibm-power/ibm-power.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ func Destroy(mCtxArgs *mc.ContextArgs) (err error) {
if err != nil {
return err
}
return ibmcloudp.Destroy(mCtx, stackIBMPowerVS)
if err := ibmcloudp.DestroyStack(mCtx, stackIBMPowerVS); err != nil {
return err
}
return ibmcloudp.CleanupState(mCtx)
}

func (r *pwRequest) deploy(ctx *pulumi.Context) error {
Expand Down
5 changes: 4 additions & 1 deletion pkg/provider/ibmcloud/action/ibm-z/ibm-z.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,10 @@ func Destroy(mCtxArgs *mc.ContextArgs) (err error) {
if err != nil {
return err
}
return ibmcloudp.Destroy(mCtx, stackIBMS390)
if err := ibmcloudp.DestroyStack(mCtx, stackIBMS390); err != nil {
return err
}
return ibmcloudp.CleanupState(mCtx)
}

func (r *zRequest) deploy(ctx *pulumi.Context) error {
Expand Down
9 changes: 9 additions & 0 deletions pkg/provider/ibmcloud/constants/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package constants

const (
EnvIBMCloudAccount = "IBMCLOUD_ACCOUNT"
EnvIBMCloudAPIKey = "IBMCLOUD_API_KEY"
EnvIBMCosAccessKeyID = "IBMCLOUD_COS_ACCESS_KEY_ID"
EnvIBMCosSecretAccessKey = "IBMCLOUD_COS_SECRET_ACCESS_KEY"
EnvIBMCosEndpoint = "IBMCLOUD_COS_ENDPOINT"
)
5 changes: 3 additions & 2 deletions pkg/provider/ibmcloud/data/piimages.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/IBM/go-sdk-core/v5/core"
mc "github.com/redhat-developer/mapt/pkg/manager/context"
icConstants "github.com/redhat-developer/mapt/pkg/provider/ibmcloud/constants"
)

const powerURLRegex = "%s.power-iaas.cloud.ibm.com"
Expand Down Expand Up @@ -45,9 +46,9 @@ func GetImage(mCtx *mc.Context, args *PiImageArgs) (*string, error) {
func piImagesClient(mCtx *mc.Context, cloudInstanceId string) (*v.IBMPIImageClient, error) {
options := &ps.IBMPIOptions{
Authenticator: &core.IamAuthenticator{
ApiKey: os.Getenv("IBMCLOUD_API_KEY"),
ApiKey: os.Getenv(icConstants.EnvIBMCloudAPIKey),
},
UserAccount: os.Getenv("IBMCLOUD_ACCOUNT"),
UserAccount: os.Getenv(icConstants.EnvIBMCloudAccount),
Zone: os.Getenv("IC_ZONE"),
URL: powerURL(os.Getenv("IC_REGION")),
Debug: mCtx.Debug(),
Expand Down
3 changes: 2 additions & 1 deletion pkg/provider/ibmcloud/data/vpcimages.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/IBM/vpc-go-sdk/vpcv1"

"github.com/IBM/go-sdk-core/v5/core"
icConstants "github.com/redhat-developer/mapt/pkg/provider/ibmcloud/constants"
)

const (
Expand Down Expand Up @@ -64,7 +65,7 @@ func vpcService() (*vpcv1.VpcV1, error) {
}
return vpcv1.NewVpcV1(&vpcv1.VpcV1Options{
Authenticator: &core.IamAuthenticator{
ApiKey: os.Getenv("IBMCLOUD_API_KEY"),
ApiKey: os.Getenv(icConstants.EnvIBMCloudAPIKey),
},
URL: serviceURL,
})
Expand Down
Loading