diff --git a/book/src/framework/components/blockchains/canton.md b/book/src/framework/components/blockchains/canton.md index f1d576c04..90359e42d 100644 --- a/book/src/framework/components/blockchains/canton.md +++ b/book/src/framework/components/blockchains/canton.md @@ -66,11 +66,9 @@ import ( "fmt" "strings" "testing" - "time" "github.com/fullstorydev/grpcurl" "github.com/go-resty/resty/v2" - "github.com/golang-jwt/jwt/v5" "github.com/jhump/protoreflect/grpcreflect" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -79,7 +77,6 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/framework" "github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain" - "github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain/canton" ) type CfgCanton struct { @@ -94,52 +91,42 @@ func TestCantonSmoke(t *testing.T) { require.NoError(t, err) t.Run("Test scan endpoint", func(t *testing.T) { - resp, err := resty.New().SetBaseURL(bc.NetworkSpecificData.CantonEndpoints.ScanAPIURL).R(). + resp, err := resty.New().SetBaseURL(bc.NetworkSpecificData.CantonData.ExternalEndpoints.ScanAPIURL).R(). Get("/v0/dso-party-id") assert.NoError(t, err) fmt.Println(resp) }) t.Run("Test registry endpoint", func(t *testing.T) { - resp, err := resty.New().SetBaseURL(bc.NetworkSpecificData.CantonEndpoints.RegistryAPIURL).R(). - Get("/metadata/v1/instruments") + resp, err := resty.New().SetBaseURL(bc.NetworkSpecificData.CantonData.ExternalEndpoints.RegistryAPIURL).R(). + Get("/registry/metadata/v1/instruments") assert.NoError(t, err) fmt.Println(resp) }) testParticipant := func(t *testing.T, name string, endpoints blockchain.CantonParticipantEndpoints) { t.Run(fmt.Sprintf("Test %s endpoints", name), func(t *testing.T) { - j, _ := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{ - Issuer: "", - Subject: fmt.Sprintf("user-%s", name), - Audience: []string{canton.AuthProviderAudience}, - ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)), - NotBefore: jwt.NewNumericDate(time.Now()), - IssuedAt: jwt.NewNumericDate(time.Now()), - ID: "", - }).SignedString([]byte(canton.AuthProviderSecret)) - // JSON Ledger API fmt.Println("Calling JSON Ledger API") - resp, err := resty.New().SetBaseURL(endpoints.JSONLedgerAPIURL).SetAuthToken(j).R(). + resp, err := resty.New().SetBaseURL(endpoints.JSONLedgerAPIURL).SetAuthToken(endpoints.JWT).R(). Get("/v2/packages") assert.NoError(t, err) fmt.Println(resp) // gRPC Ledger API - use reflection fmt.Println("Calling gRPC Ledger API") - res, err := callGRPC(t.Context(), endpoints.GRPCLedgerAPIURL, "com.daml.ledger.api.v2.admin.PartyManagementService/GetParties", `{}`, []string{fmt.Sprintf("Authorization: Bearer %s", j)}) + res, err := callGRPC(t.Context(), endpoints.GRPCLedgerAPIURL, "com.daml.ledger.api.v2.admin.PartyManagementService/GetParties", `{}`, []string{fmt.Sprintf("Authorization: Bearer %s", endpoints.JWT)}) assert.NoError(t, err) fmt.Println(res) // gRPC Admin API - use reflection fmt.Println("Calling gRPC Admin API") - res, err = callGRPC(t.Context(), endpoints.AdminAPIURL, "com.digitalasset.canton.admin.participant.v30.PackageService/ListDars", `{}`, []string{fmt.Sprintf("Authorization: Bearer %s", j)}) + res, err = callGRPC(t.Context(), endpoints.AdminAPIURL, "com.digitalasset.canton.admin.participant.v30.PackageService/ListDars", `{}`, []string{fmt.Sprintf("Authorization: Bearer %s", endpoints.JWT)}) assert.NoError(t, err) fmt.Println(res) // Validator API fmt.Println("Calling Validator API") - resp, err = resty.New().SetBaseURL(endpoints.ValidatorAPIURL).SetAuthToken(j).R(). + resp, err = resty.New().SetBaseURL(endpoints.ValidatorAPIURL).SetAuthToken(endpoints.JWT).R(). Get("/v0/admin/users") assert.NoError(t, err) fmt.Println(resp) @@ -160,9 +147,9 @@ func TestCantonSmoke(t *testing.T) { } // Call all participants, starting with the SV - testParticipant(t, "sv", bc.NetworkSpecificData.CantonEndpoints.SuperValidator) + testParticipant(t, "sv", bc.NetworkSpecificData.CantonData.ExternalEndpoints.SuperValidator) for i := 1; i <= in.BlockchainA.NumberOfCantonValidators; i++ { - testParticipant(t, fmt.Sprintf("participant%d", i), bc.NetworkSpecificData.CantonEndpoints.Participants[i-1]) + testParticipant(t, fmt.Sprintf("participant%d", i), bc.NetworkSpecificData.CantonData.ExternalEndpoints.Participants[i-1]) } } @@ -199,4 +186,4 @@ func callGRPC(ctx context.Context, url string, method string, jsonRequest string return output.String(), nil } -``` \ No newline at end of file +``` diff --git a/framework/.changeset/v0.15.0.md b/framework/.changeset/v0.15.0.md new file mode 100644 index 000000000..49bc23817 --- /dev/null +++ b/framework/.changeset/v0.15.0.md @@ -0,0 +1,3 @@ +- Update the Canton blockchain containers to support Docker-internal networking +- Change the `NetworkSpecificData` for Canton to be of type `CantonData` and split internal/external endpoints into separate fields +- Bump the Canton and Splice images to `0.5.13` diff --git a/framework/components/blockchain/blockchain.go b/framework/components/blockchain/blockchain.go index 05a4d30c6..55a729e15 100644 --- a/framework/components/blockchain/blockchain.go +++ b/framework/components/blockchain/blockchain.go @@ -92,9 +92,9 @@ type Output struct { } type NetworkSpecificData struct { - SuiAccount *SuiWalletInfo `toml:"sui_account" comment:"Sui network account info"` - CantonEndpoints *CantonEndpoints `toml:"canton_endpoints" comment:"Canton network endpoints info"` - StellarNetwork *StellarNetworkInfo `toml:"stellar_network" comment:"Stellar network info"` + SuiAccount *SuiWalletInfo `toml:"sui_account" comment:"Sui network account info"` + CantonData *CantonData `toml:"canton_data" comment:"Canton network data"` + StellarNetwork *StellarNetworkInfo `toml:"stellar_network" comment:"Stellar network info"` } // Node represents blockchain node output, URLs required for connection locally and inside docker network diff --git a/framework/components/blockchain/canton.go b/framework/components/blockchain/canton.go index 3059d89df..2dbf9388b 100644 --- a/framework/components/blockchain/canton.go +++ b/framework/components/blockchain/canton.go @@ -19,6 +19,13 @@ const ( TokenExpiry = time.Hour * 24 * 365 * 10 // 10 years ) +type CantonData struct { + // Docker internal endpoints, only reachable if connected to the same Docker network (framework.DefaultNetworkName) + InternalEndpoints CantonEndpoints `toml:"internal_endpoints" comment:"Docker-internal endpoints, only reachable from containers connected to the same networks"` + // External endpoints, reachable from the Docker host + ExternalEndpoints CantonEndpoints `toml:"external_endpoints" comment:"Docker-external endpoints, only reachable from the Docker host"` +} + type CantonEndpoints struct { // ScanAPIURL https://docs.sync.global/app_dev/scan_api/index.html ScanAPIURL string `toml:"scan_api_url" comment:"https://docs.sync.global/app_dev/scan_api/index.html"` @@ -26,7 +33,7 @@ type CantonEndpoints struct { RegistryAPIURL string `toml:"registry_api_url" comment:"https://docs.sync.global/app_dev/token_standard/index.html#api-references"` // SuperValidator The endpoints for the super validator - SuperValidator CantonParticipantEndpoints `toml:"super_validator" comment:"Canton network super validator"` + SuperValidator CantonParticipantEndpoints `toml:"super_validator" comment:"Canton super validator endpoints"` // Participants The endpoints for the participants, in order from participant1 to participantN - depending on the number of validators requested Participants []CantonParticipantEndpoints `toml:"participants" comment:"Canton participant endpoints"` } @@ -119,7 +126,7 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) { } // Set up Nginx container - nginxReq := canton.NginxContainerRequest(in.NumberOfCantonValidators, in.Port, cantonReq.Name, spliceReq.Name) + nginxReq, nginxContainerName := canton.NginxContainerRequest(in.NumberOfCantonValidators, in.Port, cantonReq.Name, spliceReq.Name) nginxContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: nginxReq, Started: true, @@ -133,6 +140,7 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) { return nil, err } + // Add SV info to output svUser := "user-sv" svToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{ Issuer: "", @@ -146,21 +154,40 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) { if err != nil { return nil, fmt.Errorf("failed to create token for sv: %w", err) } - endpoints := &CantonEndpoints{ - ScanAPIURL: fmt.Sprintf("http://scan.%s:%s/api/scan", host, in.Port), - RegistryAPIURL: fmt.Sprintf("http://scan.%s:%s", host, in.Port), // Don't add /registry to URL as this is part of the OpenAPI spec and the base URL should point to the root - SuperValidator: CantonParticipantEndpoints{ - JSONLedgerAPIURL: fmt.Sprintf("http://sv.json-ledger-api.%s:%s", host, in.Port), - GRPCLedgerAPIURL: fmt.Sprintf("sv.grpc-ledger-api.%s:%s", host, in.Port), - AdminAPIURL: fmt.Sprintf("sv.admin-api.%s:%s", host, in.Port), - ValidatorAPIURL: fmt.Sprintf("http://sv.validator-api.%s:%s/api/validator", host, in.Port), - HTTPHealthCheckURL: fmt.Sprintf("http://sv.http-health-check.%s:%s", host, in.Port), - GRPCHealthCheckURL: fmt.Sprintf("sv.grpc-health-check.%s:%s", host, in.Port), - UserID: svUser, - JWT: svToken, + data := &CantonData{ + InternalEndpoints: CantonEndpoints{ + ScanAPIURL: fmt.Sprintf("http://scan.%s:%d/api/scan", nginxContainerName, canton.DefaultNginxInternalPort), + RegistryAPIURL: fmt.Sprintf("http://scan.%s:%d", nginxContainerName, canton.DefaultNginxInternalPort), // Don't add /registry to URL as this is part of the OpenAPI spec and the base URL should point to the root + SuperValidator: CantonParticipantEndpoints{ + JSONLedgerAPIURL: fmt.Sprintf("http://sv.json-ledger-api.%s:%d", nginxContainerName, canton.DefaultNginxInternalPort), + GRPCLedgerAPIURL: fmt.Sprintf("sv.grpc-ledger-api.%s:%d", nginxContainerName, canton.DefaultNginxInternalPort), + AdminAPIURL: fmt.Sprintf("sv.admin-api.%s:%d", nginxContainerName, canton.DefaultNginxInternalPort), + ValidatorAPIURL: fmt.Sprintf("http://sv.validator-api.%s:%d/api/validator", nginxContainerName, canton.DefaultNginxInternalPort), + HTTPHealthCheckURL: fmt.Sprintf("http://sv.http-health-check.%s:%d", nginxContainerName, canton.DefaultNginxInternalPort), + GRPCHealthCheckURL: fmt.Sprintf("sv.grpc-health-check.%s:%d", nginxContainerName, canton.DefaultNginxInternalPort), + UserID: svUser, + JWT: svToken, + }, + Participants: make([]CantonParticipantEndpoints, 0, in.NumberOfCantonValidators), + }, + ExternalEndpoints: CantonEndpoints{ + ScanAPIURL: fmt.Sprintf("http://scan.%s:%s/api/scan", host, in.Port), + RegistryAPIURL: fmt.Sprintf("http://scan.%s:%s", host, in.Port), // Don't add /registry to URL as this is part of the OpenAPI spec and the base URL should point to the root + SuperValidator: CantonParticipantEndpoints{ + JSONLedgerAPIURL: fmt.Sprintf("http://sv.json-ledger-api.%s:%s", host, in.Port), + GRPCLedgerAPIURL: fmt.Sprintf("sv.grpc-ledger-api.%s:%s", host, in.Port), + AdminAPIURL: fmt.Sprintf("sv.admin-api.%s:%s", host, in.Port), + ValidatorAPIURL: fmt.Sprintf("http://sv.validator-api.%s:%s/api/validator", host, in.Port), + HTTPHealthCheckURL: fmt.Sprintf("http://sv.http-health-check.%s:%s", host, in.Port), + GRPCHealthCheckURL: fmt.Sprintf("sv.grpc-health-check.%s:%s", host, in.Port), + UserID: svUser, + JWT: svToken, + }, + Participants: make([]CantonParticipantEndpoints, 0, in.NumberOfCantonValidators), }, - Participants: nil, } + + // Add Participant info to output for i := 1; i <= in.NumberOfCantonValidators; i++ { participantUser := fmt.Sprintf("user-participant%v", i) token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{ @@ -175,7 +202,17 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) { if err != nil { return nil, fmt.Errorf("failed to create token for participant%v: %w", i, err) } - participantEndpoints := CantonParticipantEndpoints{ + data.InternalEndpoints.Participants = append(data.InternalEndpoints.Participants, CantonParticipantEndpoints{ + JSONLedgerAPIURL: fmt.Sprintf("http://participant%d.json-ledger-api.%s:%d", i, nginxContainerName, canton.DefaultNginxInternalPort), + GRPCLedgerAPIURL: fmt.Sprintf("participant%d.grpc-ledger-api.%s:%d", i, nginxContainerName, canton.DefaultNginxInternalPort), + AdminAPIURL: fmt.Sprintf("participant%d.admin-api.%s:%d", i, nginxContainerName, canton.DefaultNginxInternalPort), + ValidatorAPIURL: fmt.Sprintf("http://participant%d.validator-api.%s:%d/api/validator", i, nginxContainerName, canton.DefaultNginxInternalPort), + HTTPHealthCheckURL: fmt.Sprintf("http://participant%d.http-health-check.%s:%d", i, nginxContainerName, canton.DefaultNginxInternalPort), + GRPCHealthCheckURL: fmt.Sprintf("participant%d.grpc-health-check.%s:%d", i, nginxContainerName, canton.DefaultNginxInternalPort), + UserID: participantUser, + JWT: token, + }) + data.ExternalEndpoints.Participants = append(data.ExternalEndpoints.Participants, CantonParticipantEndpoints{ JSONLedgerAPIURL: fmt.Sprintf("http://participant%d.json-ledger-api.%s:%s", i, host, in.Port), GRPCLedgerAPIURL: fmt.Sprintf("participant%d.grpc-ledger-api.%s:%s", i, host, in.Port), AdminAPIURL: fmt.Sprintf("participant%d.admin-api.%s:%s", i, host, in.Port), @@ -184,8 +221,7 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) { GRPCHealthCheckURL: fmt.Sprintf("participant%d.grpc-health-check.%s:%s", i, host, in.Port), UserID: participantUser, JWT: token, - } - endpoints.Participants = append(endpoints.Participants, participantEndpoints) + }) } return &Output{ @@ -195,7 +231,7 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) { ChainID: in.ChainID, ContainerName: nginxReq.Name, NetworkSpecificData: &NetworkSpecificData{ - CantonEndpoints: endpoints, + CantonData: data, }, }, nil } diff --git a/framework/components/blockchain/canton/canton.go b/framework/components/blockchain/canton/canton.go index 3b3f906ed..f0512ed30 100644 --- a/framework/components/blockchain/canton/canton.go +++ b/framework/components/blockchain/canton/canton.go @@ -13,7 +13,7 @@ import ( // Canton Defaults const ( - SpliceVersion = "0.5.11" + SpliceVersion = "0.5.13" Image = "ghcr.io/digital-asset/decentralized-canton-sync/docker/canton" ) diff --git a/framework/components/blockchain/canton/nginx.go b/framework/components/blockchain/canton/nginx.go index da7693836..80d408fec 100644 --- a/framework/components/blockchain/canton/nginx.go +++ b/framework/components/blockchain/canton/nginx.go @@ -12,7 +12,8 @@ import ( ) const ( - DefaultNginxImage = "nginx:1.29.5" + DefaultNginxImage = "nginx:1.29.5" + DefaultNginxInternalPort = 8080 ) const nginxConfig = ` @@ -21,6 +22,7 @@ events { } http { + server_names_hash_bucket_size 128; include mime.types; default_type application/octet-stream; client_max_body_size 100M; @@ -53,12 +55,12 @@ http { } ` -func getNginxTemplate(numberOfValidators int) string { - template := ` +func getNginxTemplate(nginxContainerName string, nginxInternalPort int, numberOfValidators int) (template string, internalHostnames []string) { + template = fmt.Sprintf(` # SV server { - listen 8080; - server_name sv.json-ledger-api.localhost; + listen %[1]d; + server_name sv.json-ledger-api.*; location / { proxy_pass http://${CANTON_CONTAINER_NAME}:${CANTON_PARTICIPANT_JSON_API_PORT_PREFIX}00; add_header Access-Control-Allow-Origin *; @@ -68,40 +70,40 @@ server { } server { - listen 8080 http2; - server_name sv.grpc-ledger-api.localhost; + listen %[1]d http2; + server_name sv.grpc-ledger-api.*; location / { grpc_pass grpc://${CANTON_CONTAINER_NAME}:${CANTON_PARTICIPANT_LEDGER_API_PORT_PREFIX}00; } } server { - listen 8080; - server_name sv.http-health-check.localhost; + listen %[1]d; + server_name sv.http-health-check.*; location / { proxy_pass http://${CANTON_CONTAINER_NAME}:${CANTON_PARTICIPANT_HTTP_HEALTHCHECK_PORT_PREFIX}00; } } server { - listen 8080 http2; - server_name sv.grpc-health-check.localhost; + listen %[1]d http2; + server_name sv.grpc-health-check.*; location / { grpc_pass grpc://${CANTON_CONTAINER_NAME}:${CANTON_PARTICIPANT_GRPC_HEALTHCHECK_PORT_PREFIX}00; } } server { - listen 8080 http2; - server_name sv.admin-api.localhost; + listen %[1]d http2; + server_name sv.admin-api.*; location / { grpc_pass grpc://${CANTON_CONTAINER_NAME}:${CANTON_PARTICIPANT_ADMIN_API_PORT_PREFIX}00; } } server { - listen 8080; - server_name sv.validator-api.localhost; + listen %[1]d; + server_name sv.validator-api.*; location /api/validator { rewrite ^\/(.*) /$1 break; proxy_pass http://${SPLICE_CONTAINER_NAME}:${SPLICE_VALIDATOR_ADMIN_API_PORT_PREFIX}00/api/validator; @@ -109,8 +111,8 @@ server { } server { - listen 8080; - server_name scan.localhost; + listen %[1]d; + server_name scan.*; location /api/scan { rewrite ^\/(.*) /$1 break; @@ -121,17 +123,26 @@ server { proxy_pass http://${SPLICE_CONTAINER_NAME}:5012/registry; } } - ` + `, nginxInternalPort) + internalHostnames = append(internalHostnames, + fmt.Sprintf("sv.json-ledger-api.%s", nginxContainerName), + fmt.Sprintf("sv.grpc-ledger-api.%s", nginxContainerName), + fmt.Sprintf("sv.http-health-check.%s", nginxContainerName), + fmt.Sprintf("sv.grpc-health-check.%s", nginxContainerName), + fmt.Sprintf("sv.admin-api.%s", nginxContainerName), + fmt.Sprintf("sv.validator-api.%s", nginxContainerName), + fmt.Sprintf("scan.%s", nginxContainerName), + ) // Add additional validators for i := 1; i <= numberOfValidators; i++ { template += fmt.Sprintf(` -# Participant %[1]d +# Participant %[2]d server { - listen 8080; - server_name participant%[1]d.json-ledger-api.localhost; + listen %[1]d; + server_name participant%[2]d.json-ledger-api.*; location / { - proxy_pass http://${CANTON_CONTAINER_NAME}:${CANTON_PARTICIPANT_JSON_API_PORT_PREFIX}%02[1]d; + proxy_pass http://${CANTON_CONTAINER_NAME}:${CANTON_PARTICIPANT_JSON_API_PORT_PREFIX}%02[2]d; add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS'; add_header Access-Control-Allow-Headers 'Origin, Content-Type, Accept'; @@ -139,49 +150,57 @@ server { } server { - listen 8080 http2; - server_name participant%[1]d.grpc-ledger-api.localhost; + listen %[1]d http2; + server_name participant%[2]d.grpc-ledger-api.*; location / { - grpc_pass grpc://${CANTON_CONTAINER_NAME}:${CANTON_PARTICIPANT_LEDGER_API_PORT_PREFIX}%02[1]d; + grpc_pass grpc://${CANTON_CONTAINER_NAME}:${CANTON_PARTICIPANT_LEDGER_API_PORT_PREFIX}%02[2]d; } } server { - listen 8080; - server_name participant%[1]d.http-health-check.localhost; + listen %[1]d; + server_name participant%[2]d.http-health-check.*; location / { - proxy_pass http://${CANTON_CONTAINER_NAME}:${CANTON_PARTICIPANT_HTTP_HEALTHCHECK_PORT_PREFIX}%02[1]d; + proxy_pass http://${CANTON_CONTAINER_NAME}:${CANTON_PARTICIPANT_HTTP_HEALTHCHECK_PORT_PREFIX}%02[2]d; } } server { - listen 8080 http2; - server_name participant%[1]d.grpc-health-check.localhost; + listen %[1]d http2; + server_name participant%[2]d.grpc-health-check.*; location / { - grpc_pass grpc://${CANTON_CONTAINER_NAME}:${CANTON_PARTICIPANT_GRPC_HEALTHCHECK_PORT_PREFIX}%02[1]d; + grpc_pass grpc://${CANTON_CONTAINER_NAME}:${CANTON_PARTICIPANT_GRPC_HEALTHCHECK_PORT_PREFIX}%02[2]d; } } server { - listen 8080 http2; - server_name participant%[1]d.admin-api.localhost; + listen %[1]d http2; + server_name participant%[2]d.admin-api.*; location / { - grpc_pass grpc://${CANTON_CONTAINER_NAME}:${CANTON_PARTICIPANT_ADMIN_API_PORT_PREFIX}%02[1]d; + grpc_pass grpc://${CANTON_CONTAINER_NAME}:${CANTON_PARTICIPANT_ADMIN_API_PORT_PREFIX}%02[2]d; } } server { - listen 8080; - server_name participant%[1]d.validator-api.localhost; + listen %[1]d; + server_name participant%[2]d.validator-api.*; location /api/validator { rewrite ^\/(.*) /$1 break; - proxy_pass http://${SPLICE_CONTAINER_NAME}:${SPLICE_VALIDATOR_ADMIN_API_PORT_PREFIX}%02[1]d/api/validator; + proxy_pass http://${SPLICE_CONTAINER_NAME}:${SPLICE_VALIDATOR_ADMIN_API_PORT_PREFIX}%02[2]d/api/validator; } } - `, i) + `, nginxInternalPort, i) + internalHostnames = append(internalHostnames, + fmt.Sprintf("participant%d.json-ledger-api.%s", i, nginxContainerName), + fmt.Sprintf("participant%d.grpc-ledger-api.%s", i, nginxContainerName), + fmt.Sprintf("participant%d.http-health-check.%s", i, nginxContainerName), + fmt.Sprintf("participant%d.grpc-health-check.%s", i, nginxContainerName), + fmt.Sprintf("participant%d.admin-api.%s", i, nginxContainerName), + fmt.Sprintf("participant%d.validator-api.%s", i, nginxContainerName), + ) } - return template + return template, internalHostnames } func NginxContainerRequest( @@ -189,17 +208,21 @@ func NginxContainerRequest( port string, cantonContainerName string, spliceContainerName string, -) testcontainers.ContainerRequest { - nginxContainerName := framework.DefaultTCName("nginx") +) (testcontainers.ContainerRequest, string) { + nginxContainerName := framework.DefaultTCName("canton-nginx") + // Docker doesn't support DNS wildcards: https://github.com/moby/moby/issues/43442 + // In order to allow for another container to reach the Nginx container under all the defined hostnames, + // they need to be explicitly set as network aliases. + nginxTemplate, internalHostnames := getNginxTemplate(nginxContainerName, DefaultNginxInternalPort, numberOfValidators) nginxReq := testcontainers.ContainerRequest{ Image: DefaultNginxImage, Name: nginxContainerName, Networks: []string{framework.DefaultNetworkName}, NetworkAliases: map[string][]string{ - framework.DefaultNetworkName: {nginxContainerName}, + framework.DefaultNetworkName: append([]string{nginxContainerName}, internalHostnames...), }, WaitingFor: wait.ForHTTP("/readyz").WithStartupTimeout(time.Second * 10), - ExposedPorts: []string{fmt.Sprintf("%s:8080", port)}, + ExposedPorts: []string{fmt.Sprintf("%s:%d", port, DefaultNginxInternalPort)}, Env: map[string]string{ "CANTON_PARTICIPANT_HTTP_HEALTHCHECK_PORT_PREFIX": DefaultHTTPHealthcheckPortPrefix, "CANTON_PARTICIPANT_GRPC_HEALTHCHECK_PORT_PREFIX": DefaultGRPCHealthcheckPortPrefix, @@ -217,7 +240,7 @@ func NginxContainerRequest( ContainerFilePath: "/etc/nginx/nginx.conf", FileMode: 0755, }, { - Reader: strings.NewReader(getNginxTemplate(numberOfValidators)), + Reader: strings.NewReader(nginxTemplate), ContainerFilePath: "/etc/nginx/templates/participants.conf.template", FileMode: 0755, }, @@ -225,5 +248,5 @@ func NginxContainerRequest( Labels: framework.DefaultTCLabels(), } - return nginxReq + return nginxReq, nginxContainerName } diff --git a/framework/examples/myproject/smoke_canton_test.go b/framework/examples/myproject/smoke_canton_test.go index 3fdb9bd4d..69f5175af 100644 --- a/framework/examples/myproject/smoke_canton_test.go +++ b/framework/examples/myproject/smoke_canton_test.go @@ -31,13 +31,13 @@ func TestCantonSmoke(t *testing.T) { require.NoError(t, err) t.Run("Test scan endpoint", func(t *testing.T) { - resp, err := resty.New().SetBaseURL(bc.NetworkSpecificData.CantonEndpoints.ScanAPIURL).R(). + resp, err := resty.New().SetBaseURL(bc.NetworkSpecificData.CantonData.ExternalEndpoints.ScanAPIURL).R(). Get("/v0/dso-party-id") assert.NoError(t, err) fmt.Println(resp) }) t.Run("Test registry endpoint", func(t *testing.T) { - resp, err := resty.New().SetBaseURL(bc.NetworkSpecificData.CantonEndpoints.RegistryAPIURL).R(). + resp, err := resty.New().SetBaseURL(bc.NetworkSpecificData.CantonData.ExternalEndpoints.RegistryAPIURL).R(). Get("/registry/metadata/v1/instruments") assert.NoError(t, err) fmt.Println(resp) @@ -87,9 +87,9 @@ func TestCantonSmoke(t *testing.T) { } // Call all participants, starting with the SV - testParticipant(t, "sv", bc.NetworkSpecificData.CantonEndpoints.SuperValidator) + testParticipant(t, "sv", bc.NetworkSpecificData.CantonData.ExternalEndpoints.SuperValidator) for i := 1; i <= in.BlockchainA.NumberOfCantonValidators; i++ { - testParticipant(t, fmt.Sprintf("participant%d", i), bc.NetworkSpecificData.CantonEndpoints.Participants[i-1]) + testParticipant(t, fmt.Sprintf("participant%d", i), bc.NetworkSpecificData.CantonData.ExternalEndpoints.Participants[i-1]) } }