diff --git a/cmd/run/main.go b/cmd/run/main.go index 9fb9702..4ddad81 100644 --- a/cmd/run/main.go +++ b/cmd/run/main.go @@ -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() { @@ -50,13 +52,26 @@ 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, @@ -64,6 +79,7 @@ func main() { KubeconfigPath: kubeconfigPath, KubeNamespace: kubeNamespace, KubeServiceAccount: kubeServiceAccount, + KubeNamePrefix: kubeNamePrefix, } if err := run.Run(ctx); err != nil { diff --git a/cmd/run/main_test.go b/cmd/run/main_test.go index c6f0408..86a8cf4 100644 --- a/cmd/run/main_test.go +++ b/cmd/run/main_test.go @@ -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"), @@ -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", @@ -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`)) }, diff --git a/cmd/run/testdata/validchaincode/chaincode.json b/cmd/run/testdata/validchaincode/chaincode.json index f79ae76..53ffb72 100644 --- a/cmd/run/testdata/validchaincode/chaincode.json +++ b/cmd/run/testdata/validchaincode/chaincode.json @@ -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", diff --git a/internal/builder/run.go b/internal/builder/run.go index d7eb314..2708abd 100644 --- a/internal/builder/run.go +++ b/internal/builder/run.go @@ -17,6 +17,7 @@ type Run struct { KubeconfigPath string KubeNamespace string KubeServiceAccount string + KubeNamePrefix string } func (r *Run) Run(ctx context.Context) error { @@ -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( @@ -48,6 +51,7 @@ func (r *Run) Run(ctx context.Context) error { ctx, logger, secretsClient, + kubeObjectName, r.KubeNamespace, r.PeerID, chaincodeData, @@ -66,6 +70,7 @@ func (r *Run) Run(ctx context.Context) error { ctx, logger, podsClient, + kubeObjectName, r.KubeNamespace, r.KubeServiceAccount, r.PeerID, diff --git a/internal/util/env.go b/internal/util/env.go index 8e27a33..f56eb88 100644 --- a/internal/util/env.go +++ b/internal/util/env.go @@ -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" diff --git a/internal/util/fabric.go b/internal/util/fabric.go new file mode 100644 index 0000000..d86892e --- /dev/null +++ b/internal/util/fabric.go @@ -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], + } +} diff --git a/internal/util/fabric_test.go b/internal/util/fabric_test.go new file mode 100644 index 0000000..4e9a34d --- /dev/null +++ b/internal/util/fabric_test.go @@ -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", "", ""), + ) +}) diff --git a/internal/util/k8s.go b/internal/util/k8s.go index 4fb9824..4aa2593 100644 --- a/internal/util/k8s.go +++ b/internal/util/k8s.go @@ -4,11 +4,12 @@ package util import ( "context" - "crypto/sha256" "encoding/base32" "encoding/base64" "fmt" + "hash/fnv" "os" + "regexp" "strings" "time" @@ -30,6 +31,11 @@ import ( const ( namespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" + // Defaults. + DefaultNamespace string = "default" + DefaultObjectNamePrefix string = "hlfcc" + DefaultServiceAccountName string = "default" + // Mutual TLS auth client key and cert paths in the chaincode container. TLSClientKeyPath string = "/etc/hyperledger/fabric/client.key" TLSClientCertPath string = "/etc/hyperledger/fabric/client.crt" @@ -302,11 +308,7 @@ func getChaincodePodObject( Name: "certs", VolumeSource: apiv1.VolumeSource{ Secret: &apiv1.SecretVolumeSource{ - SecretName: GetValidName( - chaincodeData.MspID, - peerID, - chaincodeData.ChaincodeID, - ), + SecretName: podName, }, }, }, @@ -316,11 +318,9 @@ func getChaincodePodObject( } func getChaincodeSecretApplyConfiguration( - namespace, peerID string, + secretName, namespace, peerID string, chaincodeData *ChaincodeJSON, ) *applycorev1.SecretApplyConfiguration { - name := GetValidName(chaincodeData.MspID, peerID, chaincodeData.ChaincodeID) - annotations := map[string]string{ "fabric-builder-k8s-ccid": chaincodeData.ChaincodeID, } @@ -343,7 +343,7 @@ func getChaincodeSecretApplyConfiguration( } return applycorev1. - Secret(name, namespace). + Secret(secretName, namespace). WithAnnotations(annotations). WithLabels(labels). WithStringData(data). @@ -354,10 +354,10 @@ func ApplyChaincodeSecrets( ctx context.Context, logger *log.CmdLogger, secretsClient v1.SecretInterface, - namespace, peerID string, + secretName, namespace, peerID string, chaincodeData *ChaincodeJSON, ) error { - secret := getChaincodeSecretApplyConfiguration(namespace, peerID, chaincodeData) + secret := getChaincodeSecretApplyConfiguration(secretName, namespace, peerID, chaincodeData) result, err := secretsClient.Apply( ctx, @@ -434,26 +434,25 @@ func CreateChaincodePod( ctx context.Context, logger *log.CmdLogger, podsClient v1.PodInterface, - namespace, serviceAccount, peerID string, + objectName, namespace, serviceAccount, peerID string, chaincodeData *ChaincodeJSON, imageData *ImageJSON, ) (*apiv1.Pod, error) { - podName := GetValidName(chaincodeData.MspID, peerID, chaincodeData.ChaincodeID) podDefinition := getChaincodePodObject( imageData, namespace, serviceAccount, - podName, + objectName, peerID, chaincodeData, ) - err := deleteChaincodePod(ctx, logger, podsClient, podName, namespace, chaincodeData) + err := deleteChaincodePod(ctx, logger, podsClient, objectName, namespace, chaincodeData) if err != nil { return nil, fmt.Errorf( "unable to delete existing chaincode pod %s/%s for chaincode ID %s: %w", namespace, - podName, + objectName, chaincodeData.ChaincodeID, err, ) @@ -463,7 +462,7 @@ func CreateChaincodePod( "Creating chaincode pod for chaincode ID %s: %s/%s", chaincodeData.ChaincodeID, namespace, - podName, + objectName, ) pod, err := podsClient.Create(ctx, podDefinition, metav1.CreateOptions{}) @@ -471,7 +470,7 @@ func CreateChaincodePod( return nil, fmt.Errorf( "unable to create chaincode pod %s/%s for chaincode ID %s: %w", namespace, - podName, + objectName, chaincodeData.ChaincodeID, err, ) @@ -487,13 +486,33 @@ func CreateChaincodePod( return pod, nil } -// GetValidName returns a valid RFC 1035 label name. -func GetValidName(mspID, peerID, chaincodeID string) string { - qualifiedChaincodeID := mspID + ":" + peerID + ":" + chaincodeID - h := sha256.New() - h.Write([]byte(qualifiedChaincodeID)) - sum := h.Sum(nil) - encodedChaincodeID := base32.HexEncoding.WithPadding(base32.NoPadding).EncodeToString(sum) +// GetValidRfc1035LabelName returns a valid RFC 1035 label name with the format +// --. +func GetValidRfc1035LabelName(prefix, peerID string, chaincodeData *ChaincodeJSON) string { + const ( + maxRfc1035LabelLength = 63 + labelSeparators = 2 + ) + + runHash := fnv.New64a() + runHash.Write([]byte(prefix)) + runHash.Write([]byte(peerID)) + runHash.Write([]byte(chaincodeData.PeerAddress)) + runHash.Write([]byte(chaincodeData.MspID)) + runHash.Write([]byte(chaincodeData.ChaincodeID)) + suffix := strings.ToLower(base32.HexEncoding.WithPadding(base32.NoPadding).EncodeToString(runHash.Sum(nil))) + + // Remove unsafe characters from the chaincode package label + packageID := NewChaincodePackageID(chaincodeData.ChaincodeID) + re := regexp.MustCompile("[^-0-9a-z]") + safeLabel := re.ReplaceAllString(strings.ToLower(packageID.Label), "") + + // Make sure the chaincode package label fits in the space available, + // taking in to account the prefix, suffix, and two '-' separators + maxLabelLength := maxRfc1035LabelLength - len(prefix) - len(suffix) - labelSeparators + if maxLabelLength < len(safeLabel) { + safeLabel = safeLabel[:maxLabelLength] + } - return "cc-" + strings.ToLower(encodedChaincodeID) + return prefix + "-" + safeLabel + "-" + suffix } diff --git a/internal/util/k8s_test.go b/internal/util/k8s_test.go index 12dd849..6ab4a62 100644 --- a/internal/util/k8s_test.go +++ b/internal/util/k8s_test.go @@ -7,36 +7,117 @@ import ( ) var _ = Describe("K8s", func() { - Describe("GetValidName", func() { - It("should return a string with a maximum of 63 characters", func() { - name := util.GetValidName("CongaCongaCongaCongaCongaCongaCongaCongaCongaCongaCongaCongaOrgMsp", "CongaCongaCongaCongaCongaCongaCongaCongaCongaCongaCongaCongaOrgPeer0", "fabfabfabfabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b") - Expect(len(name)).To(BeNumerically("<=", 63)) + Describe("GetValidRfc1035LabelName", func() { + It("should return names with a maximum of 63 characters", func() { + chaincodeData := &util.ChaincodeJSON{ + ChaincodeID: "fabfabfabfabcarfabfabfabfabcarfabfabfabfabcarfabfabfabfabcarfabfabfabfabcarfabfabfabfabcarfabfabfabfabcarfabfabfabfabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b", + PeerAddress: "peer0.org1.example.com", + MspID: "CongaCongaCongaCongaCongaCongaCongaCongaCongaCongaCongaCongaOrgMsp", + } + name := util.GetValidRfc1035LabelName("hlf-k8sbuilder-ftw", "CongaCongaCongaCongaCongaCongaCongaCongaCongaCongaCongaCongaOrgPeer0", chaincodeData) + Expect(len(name)).To(Equal(63)) }) - It("should return a string which starts with an alphabetic character", func() { - name := util.GetValidName("GreenCongaOrg", "GreenCongaOrgPeer0", "fabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b") + It("should return names which starts with an alphabetic character", func() { + chaincodeData := &util.ChaincodeJSON{ + ChaincodeID: "fabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b", + PeerAddress: "peer0.org1.example.com", + MspID: "GreenCongaOrg", + } + name := util.GetValidRfc1035LabelName("hlf-k8sbuilder-ftw", "GreenCongaOrgPeer0", chaincodeData) Expect(name).To(MatchRegexp("^[a-z]")) }) - It("should return a string which ends with an alphanumeric character", func() { - name := util.GetValidName("BlueCongaOrg", "BlueCongaOrgPeer0", "fabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b") + It("should return names which end with an alphanumeric character", func() { + chaincodeData := &util.ChaincodeJSON{ + ChaincodeID: "fabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b", + PeerAddress: "peer0.org1.example.com", + MspID: "BlueCongaOrg", + } + name := util.GetValidRfc1035LabelName("hlf-k8sbuilder-ftw", "BlueCongaOrgPeer0", chaincodeData) Expect(name).To(MatchRegexp("[a-z0-9]$")) }) - It("should return a string which only contains lowercase alphanumeric characters or '-'", func() { - name := util.GetValidName("BlueCongaOrg", "BlueCongaOrgPeer0", "fabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b") + It("should return names which only contains lowercase alphanumeric characters or '-'", func() { + chaincodeData := &util.ChaincodeJSON{ + ChaincodeID: "FAB/CAR*:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b", + PeerAddress: "peer0.org1.example.com", + MspID: "BlueCongaOrg", + } + name := util.GetValidRfc1035LabelName("hlf-k8sbuilder-ftw", "BlueCongaOrgPeer0", chaincodeData) Expect(name).To(MatchRegexp("^(?:[a-z0-9]|-)+$")) }) - It("should return different names for different input", func() { - name1 := util.GetValidName("GreenCongaOrg", "GreenCongaOrgPeer0", "fabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b") - name2 := util.GetValidName("BlueCongaOrg", "BlueCongaOrgPeer0", "fabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b") + It("should return different names for the same package IDs", func() { + chaincodeData1 := &util.ChaincodeJSON{ + ChaincodeID: "fabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b", + PeerAddress: "peer0.org1.example.com", + MspID: "GreenCongaOrg", + } + chaincodeData2 := &util.ChaincodeJSON{ + ChaincodeID: "fabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b", + PeerAddress: "peer0.org2.example.org", + MspID: "BlueCongaOrg", + } + name1 := util.GetValidRfc1035LabelName("hlf-k8sbuilder-ftw", "GreenCongaOrgPeer0", chaincodeData1) + name2 := util.GetValidRfc1035LabelName("hlf-k8sbuilder-ftw", "BlueCongaOrgPeer0", chaincodeData2) + Expect(name1).NotTo(Equal(name2)) + }) + + It("should return different names for different package IDs", func() { + chaincodeData1 := &util.ChaincodeJSON{ + ChaincodeID: "fabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b", + PeerAddress: "peer0.org1.example.com", + MspID: "RedCongaOrg", + } + chaincodeData2 := &util.ChaincodeJSON{ + ChaincodeID: "go-contract:6f98c4bb29414771312eddd1a813eef583df2121c235c4797792f141a46d4b45", + PeerAddress: "peer0.org1.example.com", + MspID: "RedCongaOrg", + } + name1 := util.GetValidRfc1035LabelName("hlf-k8sbuilder-ftw", "RedCongaOrg", chaincodeData1) + name2 := util.GetValidRfc1035LabelName("hlf-k8sbuilder-ftw", "RedCongaOrg", chaincodeData2) Expect(name1).NotTo(Equal(name2)) }) It("should return deterministic names", func() { - name := util.GetValidName("CongaOrg", "CongaOrgPeer0", "fabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b") - Expect(name).To(Equal("cc-ocqvh9ir0mi0ef6urh12f3l0dar6csdmtjfhgbfvdp2d22u109r0")) + chaincodeData := &util.ChaincodeJSON{ + ChaincodeID: "fabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b", + PeerAddress: "peer0.org1.example.com", + MspID: "CongaOrg", + } + name := util.GetValidRfc1035LabelName("hlf-k8sbuilder-ftw", "CongaOrgPeer0", chaincodeData) + Expect(name).To(Equal("hlf-k8sbuilder-ftw-fabcar-iufmagu14f8q4")) + }) + + It("should return names which start with the specified prefix and a safe version of the chaincode label", func() { + chaincodeData := &util.ChaincodeJSON{ + ChaincodeID: "FAB/CAR*:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b", + PeerAddress: "peer0.org1.example.com", + MspID: "CongaOrg", + } + name := util.GetValidRfc1035LabelName("hlf-k8sbuilder-ftw", "CongaOrgPeer0", chaincodeData) + Expect(name).To(HavePrefix("hlf-k8sbuilder-ftw" + "-fabcar-")) + }) + + It("should return names which end with a 13 character extended hex hash string", func() { + chaincodeData := &util.ChaincodeJSON{ + ChaincodeID: "fabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b", + PeerAddress: "peer0.org1.example.com", + MspID: "CongaOrg", + } + name := util.GetValidRfc1035LabelName("hlf-k8sbuilder-ftw", "CongaOrgPeer0", chaincodeData) + Expect(name).To(MatchRegexp("-[0-9a-v]{13}$")) + }) + + It("should return names with the full prefix and hash, and a truncated chaincode label", func() { + chaincodeData := &util.ChaincodeJSON{ + ChaincodeID: "fabfabfabfabcarfabfabfabfabcarfabfabfabfabcarfabfabfabfabcarfabfabfabfabcarfabfabfabfabcarfabfabfabfabcarfabfabfabfabcar:cffa266294278404e5071cb91150d550dc0bf855149908a170b1169d6160004b", + PeerAddress: "peer0.org1.example.com", + MspID: "CongaCongaCongaCongaCongaCongaCongaCongaCongaCongaCongaCongaOrgMsp", + } + name := util.GetValidRfc1035LabelName("hlf-k8sbuilder-ftw", "CongaCongaCongaCongaCongaCongaCongaCongaCongaCongaCongaCongaOrgPeer0", chaincodeData) + Expect(name).To(Equal("hlf-k8sbuilder-ftw-fabfabfabfabcarfabfabfabfabcar-1sufvsaso6m7u")) }) }) })