Skip to content

Commit

Permalink
Improve kubernetes object names
Browse files Browse the repository at this point in the history
Resolves #105

Signed-off-by: James Taylor <jamest@uk.ibm.com>
  • Loading branch information
jt-nti committed May 3, 2024
1 parent f6d18df commit 3c6f7a0
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 56 deletions.
26 changes: 21 additions & 5 deletions cmd/run/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import (
"github.com/hyperledger-labs/fabric-builder-k8s/internal/builder"
"github.com/hyperledger-labs/fabric-builder-k8s/internal/log"
"github.com/hyperledger-labs/fabric-builder-k8s/internal/util"
"k8s.io/apimachinery/pkg/api/validation"
)

const (
expectedArgsLength = 3
buildOutputDirectoryArg = 1
runMetadataDirectoryArg = 2
expectedArgsLength = 3
buildOutputDirectoryArg = 1
runMetadataDirectoryArg = 2
maximumKubeNamePrefixLength = 30
)

func main() {
Expand Down Expand Up @@ -50,20 +52,34 @@ func main() {
if kubeNamespace == "" {
kubeNamespace, err = util.GetKubeNamespace()
if err != nil {
kubeNamespace = "default"
kubeNamespace = util.DefaultNamespace
}
}

kubeServiceAccount := util.GetOptionalEnv(util.ChaincodeServiceAccountVariable, "default")
kubeServiceAccount := util.GetOptionalEnv(util.ChaincodeServiceAccountVariable, util.DefaultServiceAccountName)
logger.Debugf("%s=%s", util.ChaincodeServiceAccountVariable, kubeServiceAccount)

kubeNamePrefix := util.GetOptionalEnv(util.ObjectNamePrefixVariable, util.DefaultObjectNamePrefix)
logger.Debugf("%s=%s", util.ObjectNamePrefixVariable, kubeNamePrefix)

if len(kubeNamePrefix) > maximumKubeNamePrefixLength {
logger.Printf("The FABRIC_K8S_BUILDER_OBJECT_NAME_PREFIX environment variable must be a maximum of 30 characters")
os.Exit(1)
}

if msgs := validation.NameIsDNS1035Label(kubeNamePrefix, true); len(msgs) > 0 {
logger.Printf("The FABRIC_K8S_BUILDER_OBJECT_NAME_PREFIX environment variable must be a valid DNS-1035 label: %s", msgs[0])
os.Exit(1)
}

run := &builder.Run{
BuildOutputDirectory: buildOutputDirectory,
RunMetadataDirectory: runMetadataDirectory,
PeerID: peerID,
KubeconfigPath: kubeconfigPath,
KubeNamespace: kubeNamespace,
KubeServiceAccount: kubeServiceAccount,
KubeNamePrefix: kubeNamePrefix,
}

if err := run.Run(ctx); err != nil {
Expand Down
52 changes: 44 additions & 8 deletions cmd/run/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,60 @@ import (
)

var _ = Describe("Main", func() {
DescribeTable("Running the run command produces the correct error code",
func(expectedErrorCode int, args ...string) {
It("should return an error if the CORE_PEER_ID environment variable is not set", func() {
args := []string{"BUILD_OUTPUT_DIR", "RUN_METADATA_DIR"}
command := exec.Command(runCmdPath, args...)
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
Expect(err).NotTo(HaveOccurred())

Eventually(session).Should(gexec.Exit(1))
Eventually(
session.Err,
).Should(gbytes.Say(`run \[\d+\]: Expected CORE_PEER_ID environment variable`))
})

DescribeTable("Running the run command with the wrong arguments produces the correct error",
func(args ...string) {
command := exec.Command(runCmdPath, args...)
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
Expect(err).NotTo(HaveOccurred())

Eventually(session).Should(gexec.Exit(expectedErrorCode))
Eventually(session).Should(gexec.Exit(1))
Eventually(
session.Err,
).Should(gbytes.Say(`run \[\d+\]: Expected BUILD_OUTPUT_DIR and RUN_METADATA_DIR arguments`))
},
Entry("When too few arguments are provided", 1, "BUILD_OUTPUT_DIR"),
Entry("When too few arguments are provided", "BUILD_OUTPUT_DIR"),
Entry(
"When too many arguments are provided",
1,
"BUILD_OUTPUT_DIR",
"RUN_METADATA_DIR",
"UNEXPECTED_ARGUMENT",
),
)

DescribeTable("Running the run command produces the correct error for invalid FABRIC_K8S_BUILDER_OBJECT_NAME_PREFIX environment variable values",
func(kubeNamePrefixValue, expectedErrorMessage string) {
args := []string{"BUILD_OUTPUT_DIR", "RUN_METADATA_DIR"}
command := exec.Command(runCmdPath, args...)
command.Env = append(os.Environ(),
"CORE_PEER_ID=core-peer-id-abcdefghijklmnopqrstuvwxyz-0123456789",
"FABRIC_K8S_BUILDER_OBJECT_NAME_PREFIX="+kubeNamePrefixValue,
)
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
Expect(err).NotTo(HaveOccurred())

Eventually(session).Should(gexec.Exit(1))
Eventually(
session.Err,
).Should(gbytes.Say(expectedErrorMessage))
},
Entry("When the FABRIC_K8S_BUILDER_OBJECT_NAME_PREFIX is too long", "long-prefix-is-looooooooooooooooooooong", `run \[\d+\]: The FABRIC_K8S_BUILDER_OBJECT_NAME_PREFIX environment variable must be a maximum of 30 characters`),
Entry("When the FABRIC_K8S_BUILDER_OBJECT_NAME_PREFIX contains invalid characters", "invalid/PREFIX*", `run \[\d+\]: The FABRIC_K8S_BUILDER_OBJECT_NAME_PREFIX environment variable must be a valid DNS-1035 label: a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character`),
Entry("When the FABRIC_K8S_BUILDER_OBJECT_NAME_PREFIX starts with a number", "1prefix", `run \[\d+\]: The FABRIC_K8S_BUILDER_OBJECT_NAME_PREFIX environment variable must be a valid DNS-1035 label: a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character`),
Entry("When the FABRIC_K8S_BUILDER_OBJECT_NAME_PREFIX starts with a dash", "-prefix", `run \[\d+\]: The FABRIC_K8S_BUILDER_OBJECT_NAME_PREFIX environment variable must be a valid DNS-1035 label: a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character`),
)

It(
"should start a chaincode pod using the supplied configuration environment variables",
Label("kind"),
Expand Down Expand Up @@ -59,7 +95,7 @@ var _ = Describe("Main", func() {
).Should(gbytes.Say(`run \[\d+\] DEBUG: FABRIC_K8S_BUILDER_SERVICE_ACCOUNT=chaincode`))
Eventually(
session.Err,
).Should(gbytes.Say(`run \[\d+\]: Running chaincode ID CHAINCODE_ID in kubernetes pod chaincode/cc-m4eml38l6lv2ost7v1i3q6698a5p4gu1l3lnflb4boi7jnle6lt0`))
).Should(gbytes.Say(`run \[\d+\]: Running chaincode ID CHAINCODE_LABEL:CHAINCODE_HASH in kubernetes pod chaincode/hlfcc-chaincodelabel-f15ukm9v906aq`))

pipe := script.Exec(
"kubectl wait --for=condition=ready pod --timeout=120s --namespace=chaincode -l fabric-builder-k8s-peerid=core-peer-id-abcdefghijklmnopqrstuvwxyz-0123456789",
Expand All @@ -84,8 +120,8 @@ var _ = Describe("Main", func() {
Eventually(descSession.Out).Should(gbytes.Say(`fabric-builder-k8s-mspid=MSPID`))
Eventually(
descSession.Out,
).Should(gbytes.Say(`fabric-builder-k8s-ccid:\s+CHAINCODE_ID`))
Eventually(descSession.Out).Should(gbytes.Say(`CORE_CHAINCODE_ID_NAME:\s+CHAINCODE_ID`))
).Should(gbytes.Say(`fabric-builder-k8s-ccid:\s+CHAINCODE_LABEL:CHAINCODE_HASH`))
Eventually(descSession.Out).Should(gbytes.Say(`CORE_CHAINCODE_ID_NAME:\s+CHAINCODE_LABEL:CHAINCODE_HASH`))
Eventually(descSession.Out).Should(gbytes.Say(`CORE_PEER_ADDRESS:\s+PEER_ADDRESS`))
Eventually(descSession.Out).Should(gbytes.Say(`CORE_PEER_LOCALMSPID:\s+MSPID`))
},
Expand Down
2 changes: 1 addition & 1 deletion cmd/run/testdata/validchaincode/chaincode.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"chaincode_id": "CHAINCODE_ID",
"chaincode_id": "CHAINCODE_LABEL:CHAINCODE_HASH",
"peer_address": "PEER_ADDRESS",
"client_cert": "CLIENT_CERT",
"client_key": "CLIENT_KEY",
Expand Down
5 changes: 5 additions & 0 deletions internal/builder/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type Run struct {
KubeconfigPath string
KubeNamespace string
KubeServiceAccount string
KubeNamePrefix string
}

func (r *Run) Run(ctx context.Context) error {
Expand All @@ -33,6 +34,8 @@ func (r *Run) Run(ctx context.Context) error {
return err
}

kubeObjectName := util.GetValidRfc1035LabelName(r.KubeNamePrefix, r.PeerID, chaincodeData)

clientset, err := util.GetKubeClientset(logger, r.KubeconfigPath)
if err != nil {
return fmt.Errorf(
Expand All @@ -48,6 +51,7 @@ func (r *Run) Run(ctx context.Context) error {
ctx,
logger,
secretsClient,
kubeObjectName,
r.KubeNamespace,
r.PeerID,
chaincodeData,
Expand All @@ -66,6 +70,7 @@ func (r *Run) Run(ctx context.Context) error {
ctx,
logger,
podsClient,
kubeObjectName,
r.KubeNamespace,
r.KubeServiceAccount,
r.PeerID,
Expand Down
1 change: 1 addition & 0 deletions internal/util/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
const (
builderVariablePrefix = "FABRIC_K8S_BUILDER_"
ChaincodeNamespaceVariable = builderVariablePrefix + "NAMESPACE"
ObjectNamePrefixVariable = builderVariablePrefix + "OBJECT_NAME_PREFIX"
ChaincodeServiceAccountVariable = builderVariablePrefix + "SERVICE_ACCOUNT"
DebugVariable = builderVariablePrefix + "DEBUG"
KubeconfigPathVariable = "KUBECONFIG_PATH"
Expand Down
28 changes: 28 additions & 0 deletions internal/util/fabric.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: Apache-2.0

package util

import "strings"

type ChaincodePackageID struct {
Label string
Hash string
}

// NewChaincodePackageID returns a ChaincodePackageID created from the provided string.
func NewChaincodePackageID(chaincodeID string) *ChaincodePackageID {
substrings := strings.Split(chaincodeID, ":")

// If it doesn't look like a label and a hash, don't try and guess which is which
if len(substrings) == 1 {
return &ChaincodePackageID{
Label: "",
Hash: "",
}
}

return &ChaincodePackageID{
Label: strings.Join(substrings[:len(substrings)-1], ":"),
Hash: substrings[len(substrings)-1],
}
}
23 changes: 23 additions & 0 deletions internal/util/fabric_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package util_test

import (
"github.com/hyperledger-labs/fabric-builder-k8s/internal/util"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("Fabric", func() {
DescribeTable("NewChaincodePackageID return a new ChaincodePackageID with the expected label and hash values",
func(chaincodeID, expectedLabel, expectedHash string) {
packageID := util.NewChaincodePackageID(chaincodeID)
Expect(packageID.Label).To(Equal(expectedLabel), "The ChaincodePackageID should include the expected label")
Expect(packageID.Hash).To(Equal(expectedHash), "The ChaincodePackageID should include the expected hash")
},
Entry("When the chaincode ID only contains one colon", "fabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b", "fabcar", "cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b"),
// The rest are a bit of a guess since I'm not sure the package ID format is defined in detail anywhere
Entry("When the chaincode ID contains more than one colon", "fab:car:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b", "fab:car", "cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b"),
Entry("When the chaincode ID contains a double colon", "fab::car:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b", "fab::car", "cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b"),
Entry("When the chaincode ID is an empty string", "", "", ""),
Entry("When the chaincode ID does not contain a colon", "fabcar", "", ""),
)
})
Loading

0 comments on commit 3c6f7a0

Please sign in to comment.