Skip to content
Merged
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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Learn more:
- Create signed and encrypted contracts
- Support contract expiry with CA certificates
- Validate contract schemas
- Create Gzipped & Encoded initdata for HPCC Peerpod

- **Archive Management**
- Generate Base64 tar archives of `docker-compose.yaml` or `pods.yaml`
Expand All @@ -75,6 +76,7 @@ Learn more:
- Validate network-config schemas for on-premise deployments
- Support HPVS, HPCR RHVS, and HPCC Peer Pod configurations


## Installation

Download the CLI tool for your operating system from the [releases page](https://github.com/ibm-hyper-protect/contract-cli/releases/latest).
Expand Down Expand Up @@ -168,6 +170,14 @@ contract-cli validate-contract \
--type hpvs
```

### Create initdata annotation from signed & encrypted contract

```bash
# Create initdata annotation
contract-cli initdata \
--in signed_encrypted_contract.yaml
```

## Usage

```bash
Expand All @@ -194,6 +204,7 @@ Available Commands:
encrypt-string Encrypt string in Hyper Protect format
get-certificate Extract specific certificate version from download output
help Help about any command
initdata Gzip and Encoded initdata annotation
image Get HPCR image details from IBM Cloud
validate-contract Validate contract schema
validate-encryption-certificate validate encryption certificate
Expand Down Expand Up @@ -230,6 +241,7 @@ The [`samples/`](samples/) directory contains example configurations:
- [Attestation Records](samples/attestation/)
- [Network Configuration](samples/network/)
- [Docker Compose Examples](samples/tgz/)
- [Sample Singed & Encrypted Contract](samples/hpcc/signed-encrypt-hpcc.yaml)

## Related Projects

Expand Down
60 changes: 60 additions & 0 deletions cmd/initdata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) 2025 IBM Corp.
// All rights reserved.
//
// 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 cmd

import (
"log"

"github.com/ibm-hyper-protect/contract-cli/common"
"github.com/ibm-hyper-protect/contract-cli/lib/initdata"
"github.com/spf13/cobra"
)

// initdataCmd represents the hpccinidata command
var initdataCmd = &cobra.Command{
Use: initdata.ParameterName,
Short: initdata.ParameterShortDescription,
Long: initdata.ParameterLongDescription,
Run: func(cmd *cobra.Command, args []string) {
inputDataPath, outputPath, err := initdata.ValidateInput(cmd)
if err != nil {
log.Fatal(err)
}

gzipInitdata, err := initdata.GenerateInitdata(inputDataPath)
if err != nil {
log.Fatal(err)
}

err = initdata.PrintInitdata(gzipInitdata, outputPath)
if err != nil {
log.Fatal(err)
}
},
}

// init - cobra init function
func init() {
rootCmd.AddCommand(initdataCmd)
requiredFlags := map[string]bool{
"in": true,
}

initdataCmd.PersistentFlags().String(initdata.InputFlagName, "", initdata.InputFlagDescription)
initdataCmd.PersistentFlags().String(initdata.OutputFlagName, "", initdata.OutputFlagDescription)
common.SetCustomHelpTemplate(initdataCmd, requiredFlags)
common.SetCustomErrorTemplate(initdataCmd)
}
47 changes: 47 additions & 0 deletions cmd/initdata_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) 2025 IBM Corp.
// All rights reserved.
//
// 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 cmd

import (
"bytes"
"testing"

"github.com/ibm-hyper-protect/contract-cli/lib/initdata"
"github.com/stretchr/testify/assert"
)

const (
//Initdata Test Case.
sampleSignedEncryptedContract = "../samples/hpcc/signed-encrypt-hpcc.yaml"
sampleGzippedInitdataValue = "../build/gzipped-initdata"
)

var (
sampleGzippedInitdataCmd = []string{initdata.ParameterName, "--in", sampleSignedEncryptedContract, "--out", sampleGzippedInitdataValue}
)

// Testcase to check gzipped initdata funtionality.
func TestGzippedInitdata(t *testing.T) {
// Capture output
buf := new(bytes.Buffer)
rootCmd.SetOut(buf)
rootCmd.SetErr(buf)

rootCmd.SetArgs(sampleGzippedInitdataCmd)
err := initdataCmd.Execute()

assert.NoError(t, err)
}
28 changes: 28 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Complete command reference and usage guide for the Hyper Protect Contract CLI.
- [image](#image)
- [validate-contract](#validate-contract)
- [validate-network](#validate-network)
- [initdata](#initdata)
- [Common Workflows](#common-workflows)
- [Troubleshooting](#troubleshooting)
- [Examples](#examples)
Expand Down Expand Up @@ -609,6 +610,32 @@ contract-cli validate-encryption-certificate --in encryption-cert.crt

---

### initdata
Create initdata annotation from signed and encrypted contract for Hyper Protect Confidential Containers PeerPod solution

#### Usage

```bash
contract-cli initdata [flags]
```

#### Flags

| Flag | Type | Required | Description |
|------|------|----------|-------------|
| `--in` | string | Yes | Path to signed & encrypted contract YAML file |
| `--out` | string | No | Path to store gzipped & encoded initdata value |
| `-h, --help` | - | No | Display help information |

#### Examples

**Create Hpcc Initdata from signed & encrypted contract**
```bash
contract-cli initdata --in signed_encrypted_contract.yaml
```

---

## Common Workflows

### Complete Contract Generation Workflow
Expand Down Expand Up @@ -731,6 +758,7 @@ The [`samples/`](../samples/) directory contains working examples:
- **[Attestation Records](../samples/attestation/)** - Example attestation files
- **[Network Configuration](../samples/network/)** - Network config examples
- **[Docker Compose](../samples/tgz/)** - Compose file examples
- **[Signed & Encrypted Contract](../samples/hpcc/signed-encrypt-hpcc.yaml)** - Signed & Encrypted hpcc contract

---

Expand Down
85 changes: 85 additions & 0 deletions lib/initdata/initdata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (c) 2025 IBM Corp.
// All rights reserved.
//
// 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 initdata

import (
"fmt"

"github.com/ibm-hyper-protect/contract-cli/common"
"github.com/ibm-hyper-protect/contract-go/v2/contract"
"github.com/spf13/cobra"
)

const (
ParameterName = "initdata"
ParameterShortDescription = "Gzip and Encoded initdata annotation"
ParameterLongDescription = `Gzip and Encoded initdata annotation`

InputFlagName = "in"
InputFlagDescription = "Path of Signed and Encrypted contract"

OutputFlagName = "out"
OutputFlagDescription = "Path to save Gzipped and encoded initdata value"
)

// ValidateInput - function to validate inputs of initdata
func ValidateInput(cmd *cobra.Command) (string, string, error) {
inputData, err := cmd.Flags().GetString(InputFlagName)
if err != nil {
return "", "", err
}

if inputData == "" {
err := fmt.Errorf("Error: required flag '--in' is missing")
common.SetMandatoryFlagError(cmd, err)
}

outputPath, err := cmd.Flags().GetString(OutputFlagName)
if err != nil {
return "", "", err
}
return inputData, outputPath, nil
}

// GenerateInitdata - function to generate gzipped initdata
func GenerateInitdata(inputDataPath string) (string, error) {
if !common.CheckFileFolderExists(inputDataPath) {
return "", fmt.Errorf("the contract path doesn't exist")
}
inputData, err := common.ReadDataFromFile(inputDataPath)
if err != nil {
return "", err
}
gzipInitdata, _, _, err := contract.HpccInitdata(inputData)
if err != nil {
return "", err
}
return gzipInitdata, nil
}

// PrintInitdata - function to print generated gzipped initdata value
func PrintInitdata(gzippedData, outputPath string) error {
if outputPath != "" {
err := common.WriteDataToFile(outputPath, gzippedData)
if err != nil {
return err
}
fmt.Println("Successfully generated gzipped initdata annotation")
} else {
fmt.Println(gzippedData)
}
return nil
}
3 changes: 3 additions & 0 deletions samples/hpcc/signed-encrypt-hpcc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
env: hyper-protect-basic.njLuN8QAL9VceUyiYmDzSyAPvVowCTyw4qDJb/Y3z8sR884gPPjHESXnkJAgDnLRl1P24xVzOXe117IQGp+ayQyrHA+40dA+RF64WNs2ZGPHoVmrIfsrEnS8WjrHla00vaN00xCdoA2DehlVYWDISN/HD9vcdPzh7nc6oPoU9/iyWuNWNJUp/QOuJipg5Yu2he5eBf0R7zVdMghKiJQ86sW1blgsUIiWnZsy9Hia+yHLmDE4OsnkgciJJYMvzarMWHOjwwEyhKPNxoVGfxM8f2pp13gx9QY2tWGIVSDFqyWJudAKZ8yvDEVL3x1FlpxG9zcMFDmAbviZhKykMaDmbDeH60+SPneyUV7x/X132d+Nyd2DvCMS/37Z3vmqfpJSxsam5UCT68f1t5Znmk4MNBFcnMI3wFkdTfazUO+2qk1LwgLM2ZFdajtgIhMLua1UOTITiHRbvmAcLppucA7w1ZkqB2gFZCVVKvIcmOHeLYPklZiHTYPnQHlLeS90sk9YoM0BNL42L7JrtTs6EVjSAgVQxDruiERbsjpQy9KwltLOVjYEDQ4v0fpAxxg6GpKds3j9vcOI+9RlABTW10RgrIZsC+QjaB+mra1PFHvcHITkPjbSmtoXXDmD22YiNReK+bYpikvEOgFRi6srrYzOG6oNDygMUQ6+PIwncwkI9V0=.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
envWorkloadSignature: ZM25jpFqS3/MRpYipUaTOW0Yy35jSo/SwuXMQfZJQnID6hMPwPS1hLZee5ST719J/GmpWmxah0PYbLZ2yf2qjzlnmJjH66aJfKhfAYSN93yT5rp++4sMlByyAavHNYcBNTs1lytMUbWP4U5zVuiIONgiGqPDGluGmNMOvH22lB7hCQxAcC1AZzv23ye2eYwPfVsFspwpe72JGn9UxSVVKHPntv/3Tok6UOoU/pXiR/eprodT3wQMbSCBqkB0xRn7pQyWF/5Mptw4mIqmnVPvCJDFZH4ksOy7+6yjfLvA6ce2OFzRIKDDEL7C/5KUFc15uCBS/Qs8D5xIDmbO0zwLw91gw+6F9JS6g63MjTWcwIF6bcF79Iu7qxJdukR5LYYgCZBFzE6Eae/31Mr6G1v6b48NEub6njcdF7b0gnUbvKEF0yD9NfocoDIiqXPXWRehxMgyTdRqQEVhO8shLny+PZmYvh0QiVJsEf3QWfEpiVVDw+wOfqTok9nheTlGsAEl0gTOgIwbULdKIyaGn+jdECdC1augYA5ebz/cOHWV9n0PjtuDeLdjTEHpKZysYoagF9RLf93nLbd7Nvr6I9Hmp2WvuUFTV8sXbuAAzZSaBv3S1Po+VtZjB4B4Y5+eV2G/JDqmUyU8RRfCiZWAcBW5GJMue9sGAio10iUTA+oliXc=
workload: hyper-protect-basic.et0k+AL9qgf9kSy+iH8z+b+3hRN9nw2J/T7zDeLcXfSfTAh31X2wAA4l4P7jqT23NFhjiJNApjy6J0xLVqHGnIGbINYTLchupBDfWSPCIh185OIBZqCxWawEt7xsbW2XNtjhQ50ErzPn1SB5pejnimZdYsvv+CLzJGZOWvoBnboBaQML60opp0jYNkFQtj6RRiTMfIAyTxNB0MKuvaJ3HDUK4mgrb1jAZ45tUDOJ6fs2M7r8sgNXojGLuqq2AMc6UrftIsBMehwwfZpxSE4wLdKhFyYMqdiZwSA6UEHBtReTGt2M1JtmZRb6DvPbH7bGqWQ3bOiQ5o+cC6M1E5C5hFC5yzMid8rQrZX13nG9JmEvUMduYAVwtIDl1lobW/FwIKhSEvphYJ5Hy/Xu7kJTBm15xIpDSfZ/MGbihtpd+J812sKEUIquwuoVgn1FlOoLtp8ZwJoM6/WBVilq/QkM7maqPwhKETLDVQh7OazpVJ6lGtvCa7kf93ojEoqqrGI3ok3IxnHAOLVdj7mG65Qx4AYdgqYoMOfX5DXe6Kv5Kzcki/3vn8YCwpD1bpxpbgM5QMx02i/Y20QAl5sjquq5zSaH7kDpVmHz3C4UyrDPxgpdC+/U7gDty9UVVuF/S8gG60zCiM2JPvWNJf8l3pYLt3P1C51zD2MGyz1FTd3ntfY=.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
Loading