|
| 1 | +## Developing Components |
| 2 | + |
| 3 | +To build a scalable framework that enables the reuse of our product deployments (contracts or services in Docker), we need to establish a clear component structure. |
| 4 | +``` |
| 5 | +package mycomponent |
| 6 | +
|
| 7 | +import ( |
| 8 | + "fmt" |
| 9 | + "github.com/smartcontractkit/chainlink-testing-framework/framework" |
| 10 | +) |
| 11 | +
|
| 12 | +type Input struct { |
| 13 | + // inputs fields that component exposes for configuration |
| 14 | + ... |
| 15 | + // outputs are embedded into inputs so framework can automatically save them |
| 16 | + Out *Output `toml:"out"` |
| 17 | +} |
| 18 | +
|
| 19 | +type Output struct { |
| 20 | + UseCache bool `toml:"use_cache"` |
| 21 | + // outputs that will be dumped to config and cached |
| 22 | +} |
| 23 | +
|
| 24 | +
|
| 25 | +func NewComponent(input *Input) (*Output, error) { |
| 26 | + if input.Out.UseCache { |
| 27 | + return input.Out, nil |
| 28 | + } |
| 29 | + |
| 30 | + // component logic here |
| 31 | + // deploy a docker container(s) |
| 32 | + // or deploy a set of smart contracts |
| 33 | + |
| 34 | + input.Out = &Output{ |
| 35 | + UseCache: true, |
| 36 | + // other fields |
| 37 | + ... |
| 38 | + } |
| 39 | + return out, nil |
| 40 | +} |
| 41 | +``` |
| 42 | +Each component can define inputs and outputs, following these rules: |
| 43 | + |
| 44 | +- Outputs should be included within inputs. |
| 45 | +- If your component is used for side effects output can be omitted. |
| 46 | +- `input.Out.UseCache` should be added if you'd like to use caching, see more [here](caching) |
| 47 | + |
| 48 | +### Docker components good practices for [testcontainers-go](https://golang.testcontainers.org/): |
| 49 | + |
| 50 | +An example [simple component](components/blockchain/anvil.go) |
| 51 | + |
| 52 | +An example of [complex component](components/clnode/clnode.go) |
| 53 | + |
| 54 | +An example of [composite component](components/node_set_extended/don.go) |
| 55 | + |
| 56 | +- Inputs should include at least `image`, `tag` and `pull_image` field |
| 57 | +``` |
| 58 | + Image string `toml:"image" validate:"required"` |
| 59 | + Tag string `toml:"tag" validate:"required"` |
| 60 | + PullImage bool `toml:"pull_image" validate:"required"` |
| 61 | +``` |
| 62 | + |
| 63 | +- `ContainerRequest` must contain labels, network and alias required for local observability stack and deployment isolation |
| 64 | +``` |
| 65 | + Labels: framework.DefaultTCLabels(), |
| 66 | + Networks: []string{framework.DefaultNetworkName}, |
| 67 | + NetworkAliases: map[string][]string{ |
| 68 | + framework.DefaultNetworkName: {containerName}, |
| 69 | + }, |
| 70 | +``` |
| 71 | +- In order to copy files into container use `framework.WriteTmpFile(data string, fileName string)` |
| 72 | +``` |
| 73 | + userSecretsOverridesFile, err := WriteTmpFile(in.Node.UserSecretsOverrides, "user-secrets-overrides.toml") |
| 74 | + if err != nil { |
| 75 | + return nil, err |
| 76 | + } |
| 77 | +``` |
| 78 | +- Output of docker component must contain all the URLs component exposes for access, both for internal docker usage and external test (host) usage |
| 79 | +``` |
| 80 | + host, err := framework.GetHost(c) |
| 81 | + if err != nil { |
| 82 | + return nil, err |
| 83 | + } |
| 84 | + mp, err := c.MappedPort(ctx, nat.Port(bindPort)) |
| 85 | + if err != nil { |
| 86 | + return nil, err |
| 87 | + } |
| 88 | +
|
| 89 | + return &NodeOut{ |
| 90 | + UseCache: true, |
| 91 | + DockerURL: fmt.Sprintf("http://%s:%s", containerName, in.Node.Port), |
| 92 | + HostURL: fmt.Sprintf("http://%s:%s", host, mp.Port()), |
| 93 | + }, nil |
| 94 | +``` |
0 commit comments