Skip to content

Commit 5625967

Browse files
authored
[DX-1118] make Chip Ingress more robust, pin Red Panda image versions (#1946)
1 parent b8dfb3c commit 5625967

File tree

4 files changed

+79
-37
lines changed

4 files changed

+79
-37
lines changed

framework/.changeset/v0.9.9.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Make Chip Ingress more robust, pin Red Panda image versions

framework/components/dockercompose/chip_ingress_set/chip_ingress.go

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import (
1111

1212
networkTypes "github.com/docker/docker/api/types/network"
1313
"github.com/docker/docker/client"
14+
"github.com/docker/go-connections/nat"
1415
"github.com/pkg/errors"
1516
"github.com/smartcontractkit/chainlink-testing-framework/framework"
17+
"github.com/testcontainers/testcontainers-go"
1618
"github.com/testcontainers/testcontainers-go/modules/compose"
1719
"github.com/testcontainers/testcontainers-go/wait"
1820
)
@@ -111,7 +113,9 @@ func New(in *Input) (*Output, error) {
111113
wait.ForListeningPort(DEFAULT_CHIP_INGRESS_GRPC_PORT),
112114
).WithDeadline(1*time.Minute),
113115
).WaitForService(DEFAULT_RED_PANDA_SERVICE_NAME,
114-
wait.ForListeningPort(DEFAULT_RED_PANDA_KAFKA_PORT).WithStartupTimeout(1*time.Minute),
116+
wait.ForAll(
117+
wait.ForListeningPort(DEFAULT_RED_PANDA_SCHEMA_REGISTRY_PORT).WithStartupTimeout(1*time.Minute),
118+
wait.ForListeningPort(DEFAULT_RED_PANDA_KAFKA_PORT).WithStartupTimeout(1*time.Minute)),
115119
).WaitForService(DEFAULT_RED_PANDA_CONSOLE_SERVICE_NAME,
116120
wait.ForListeningPort(DEFAULT_RED_PANDA_CONSOLE_PORT).WithStartupTimeout(1*time.Minute),
117121
)
@@ -130,41 +134,17 @@ func New(in *Input) (*Output, error) {
130134
}
131135
defer cli.Close()
132136

133-
timeout := time.After(1 * time.Minute)
134-
tick := time.Tick(500 * time.Millisecond)
135-
136137
// so let's try to connect to a Docker network a couple of times, there must be a race condition in Docker
137138
// and even when network sandbox has been created and container is running, this call can still fail
138139
// retrying is simpler than trying to figure out how to correctly wait for the network sandbox to be ready
139-
var connectNetwork = func(networkName string) error {
140-
for {
141-
select {
142-
case <-timeout:
143-
return fmt.Errorf("timeout while trying to connect chip-ingress to default network")
144-
case <-tick:
145-
if networkErr := cli.NetworkConnect(
146-
ctx,
147-
networkName,
148-
chipIngressContainer.ID,
149-
&networkTypes.EndpointSettings{
150-
Aliases: []string{identifier},
151-
},
152-
); networkErr != nil && !strings.Contains(networkErr.Error(), "already exists in network") {
153-
framework.L.Trace().Msgf("failed to connect to default network: %v", networkErr)
154-
continue
155-
}
156-
framework.L.Trace().Msgf("connected to %s network", networkName)
157-
return nil
158-
}
159-
}
160-
}
161-
162140
networks := []string{framework.DefaultNetworkName}
163141
networks = append(networks, in.ExtraDockerNetworks...)
164142

165143
for _, networkName := range networks {
166144
framework.L.Debug().Msgf("Connecting chip-ingress to %s network", networkName)
167-
if connectErr := connectNetwork(networkName); connectErr != nil {
145+
connectContex, connectCancel := context.WithTimeout(ctx, 30*time.Second)
146+
defer connectCancel()
147+
if connectErr := connectNetwork(connectContex, 30*time.Second, cli, chipIngressContainer.ID, networkName, identifier); connectErr != nil {
168148
return nil, errors.Wrapf(connectErr, "failed to connect chip-ingress to %s network", networkName)
169149
}
170150
// verify that the container is connected to framework's network
@@ -177,14 +157,17 @@ func New(in *Input) (*Output, error) {
177157
if !ok {
178158
return nil, fmt.Errorf("container %s is NOT on network %s", chipIngressContainer.ID, networkName)
179159
}
160+
161+
framework.L.Debug().Msgf("Container %s is connected to network %s", chipIngressContainer.ID, networkName)
180162
}
181163

182164
// get hosts and ports for chip-ingress and redpanda
183165
chipIngressExternalHost, chipIngressExternalHostErr := chipIngressContainer.Host(ctx)
184166
if chipIngressExternalHostErr != nil {
185167
return nil, errors.Wrap(chipIngressExternalHostErr, "failed to get host for Chip Ingress")
186168
}
187-
chipIngressExternalPort, chipIngressExternalPortErr := chipIngressContainer.MappedPort(ctx, DEFAULT_CHIP_INGRESS_GRPC_PORT)
169+
// for some magical reason mapped port sometimes cannot be found, even though we wait for it to be ready, when starting the services
170+
chipIngressExternalPort, chipIngressExternalPortErr := findMappdePort(ctx, 20*time.Second, chipIngressContainer, DEFAULT_CHIP_INGRESS_GRPC_PORT)
188171
if chipIngressExternalPortErr != nil {
189172
return nil, errors.Wrap(chipIngressExternalPortErr, "failed to get mapped port for Chip Ingress")
190173
}
@@ -198,11 +181,11 @@ func New(in *Input) (*Output, error) {
198181
if redpandaExternalHostErr != nil {
199182
return nil, errors.Wrap(redpandaExternalHostErr, "failed to get host for Red Panda")
200183
}
201-
redpandaExternalKafkaPort, redpandaExternalKafkaPortErr := redpandaContainer.MappedPort(ctx, DEFAULT_RED_PANDA_KAFKA_PORT)
184+
redpandaExternalKafkaPort, redpandaExternalKafkaPortErr := findMappdePort(ctx, 20*time.Second, redpandaContainer, DEFAULT_RED_PANDA_KAFKA_PORT)
202185
if redpandaExternalKafkaPortErr != nil {
203186
return nil, errors.Wrap(redpandaExternalKafkaPortErr, "failed to get mapped port for Red Panda")
204187
}
205-
redpandaExternalSchemaRegistryPort, redpandaExternalSchemaRegistryPortErr := redpandaContainer.MappedPort(ctx, DEFAULT_RED_PANDA_SCHEMA_REGISTRY_PORT)
188+
redpandaExternalSchemaRegistryPort, redpandaExternalSchemaRegistryPortErr := findMappdePort(ctx, 20*time.Second, redpandaContainer, DEFAULT_RED_PANDA_SCHEMA_REGISTRY_PORT)
206189
if redpandaExternalSchemaRegistryPortErr != nil {
207190
return nil, errors.Wrap(redpandaExternalSchemaRegistryPortErr, "failed to get mapped port for Red Panda")
208191
}
@@ -211,12 +194,11 @@ func New(in *Input) (*Output, error) {
211194
if redpandaConsoleErr != nil {
212195
return nil, errors.Wrap(redpandaConsoleErr, "failed to get redpanda-console container")
213196
}
214-
215197
redpandaExternalConsoleHost, redpandaExternalConsoleHostErr := redpandaConsoleContainer.Host(ctx)
216198
if redpandaExternalConsoleHostErr != nil {
217199
return nil, errors.Wrap(redpandaExternalConsoleHostErr, "failed to get host for Red Panda Console")
218200
}
219-
redpandaExternalConsolePort, redpandaExternalConsolePortErr := redpandaConsoleContainer.MappedPort(ctx, DEFAULT_RED_PANDA_CONSOLE_PORT)
201+
redpandaExternalConsolePort, redpandaExternalConsolePortErr := findMappdePort(ctx, 20*time.Second, redpandaConsoleContainer, DEFAULT_RED_PANDA_CONSOLE_PORT)
220202
if redpandaExternalConsolePortErr != nil {
221203
return nil, errors.Wrap(redpandaExternalConsolePortErr, "failed to get mapped port for Red Panda Console")
222204
}
@@ -266,3 +248,59 @@ func composeFilePath(rawFilePath string) (string, error) {
266248

267249
return tempFile.Name(), nil
268250
}
251+
252+
func findMappdePort(ctx context.Context, timeout time.Duration, container *testcontainers.DockerContainer, port nat.Port) (nat.Port, error) {
253+
forCtx, cancel := context.WithTimeout(ctx, timeout)
254+
defer cancel()
255+
256+
tickerInterval := 5 * time.Second
257+
ticker := time.NewTicker(5 * time.Second)
258+
defer ticker.Stop()
259+
260+
for {
261+
select {
262+
case <-forCtx.Done():
263+
return "", fmt.Errorf("timeout while waiting for mapped port for %s", port)
264+
case <-ticker.C:
265+
portCtx, portCancel := context.WithTimeout(ctx, tickerInterval)
266+
defer portCancel()
267+
mappedPort, mappedPortErr := container.MappedPort(portCtx, port)
268+
if mappedPortErr != nil {
269+
return "", errors.Wrapf(mappedPortErr, "failed to get mapped port for %s", port)
270+
}
271+
if mappedPort.Port() == "" {
272+
return "", fmt.Errorf("mapped port for %s is empty", port)
273+
}
274+
return mappedPort, nil
275+
}
276+
}
277+
}
278+
279+
func connectNetwork(connCtx context.Context, timeout time.Duration, dockerClient *client.Client, containerID, networkName, stackIdentifier string) error {
280+
ticker := time.NewTicker(500 * time.Millisecond)
281+
defer ticker.Stop()
282+
283+
networkCtx, networkCancel := context.WithTimeout(connCtx, timeout)
284+
defer networkCancel()
285+
286+
for {
287+
select {
288+
case <-networkCtx.Done():
289+
return fmt.Errorf("timeout while trying to connect chip-ingress to default network")
290+
case <-ticker.C:
291+
if networkErr := dockerClient.NetworkConnect(
292+
connCtx,
293+
networkName,
294+
containerID,
295+
&networkTypes.EndpointSettings{
296+
Aliases: []string{stackIdentifier},
297+
},
298+
); networkErr != nil && !strings.Contains(networkErr.Error(), "already exists in network") {
299+
framework.L.Trace().Msgf("failed to connect to default network: %v", networkErr)
300+
continue
301+
}
302+
framework.L.Trace().Msgf("connected to %s network", networkName)
303+
return nil
304+
}
305+
}
306+
}

framework/components/dockercompose/chip_ingress_set/docker-compose.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
services:
33

44
chip-ingress:
5-
image: chip-ingress:qa-latest
5+
# added image (instead of build) to use image created by a setup script
6+
image: ${CHIP_INGRESS_IMAGE:-chip-ingress:local-cre}
67
container_name: chip-ingress
78
depends_on:
89
- redpanda-0
@@ -42,7 +43,8 @@ services:
4243
retries: 10
4344

4445
redpanda-0:
45-
image: docker.redpanda.com/redpandadata/redpanda
46+
# using a specific version of the redpanda image to exclude potentially breaking changes
47+
image: docker.redpanda.com/redpandadata/redpanda:v24.3.16
4648
container_name: redpanda-0
4749
hostname: redpanda-0
4850
command:
@@ -72,8 +74,9 @@ services:
7274
start_period: 1s
7375

7476
redpanda-console:
77+
# using a specific version of the console image to ensure compatibility, because v3.x.x uses incompatible configuration format
7578
container_name: redpanda-console
76-
image: docker.redpanda.com/redpandadata/console
79+
image: docker.redpanda.com/redpandadata/console:v2.8.6
7780
entrypoint: /bin/sh
7881
command: -c 'echo "$$CONSOLE_CONFIG_FILE" > /tmp/config.yml; /app/console'
7982
environment:

framework/components/dockercompose/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ replace github.com/smartcontractkit/chainlink-testing-framework/framework => ../
77
require (
88
github.com/confluentinc/confluent-kafka-go v1.9.2
99
github.com/docker/docker v28.0.4+incompatible
10+
github.com/docker/go-connections v0.5.0
1011
github.com/google/go-github/v72 v72.0.0
1112
github.com/pkg/errors v0.9.1
1213
github.com/smartcontractkit/chainlink-testing-framework/framework v0.0.0-00010101000000-000000000000
@@ -64,7 +65,6 @@ require (
6465
github.com/docker/distribution v2.8.3+incompatible // indirect
6566
github.com/docker/docker-credential-helpers v0.8.2 // indirect
6667
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect
67-
github.com/docker/go-connections v0.5.0 // indirect
6868
github.com/docker/go-metrics v0.0.1 // indirect
6969
github.com/docker/go-units v0.5.0 // indirect
7070
github.com/ebitengine/purego v0.8.2 // indirect

0 commit comments

Comments
 (0)