|
| 1 | +package environment |
| 2 | + |
| 3 | +import ( |
| 4 | + "fmt" |
| 5 | + "os" |
| 6 | + |
| 7 | + pkgerrors "github.com/pkg/errors" |
| 8 | + |
| 9 | + capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" |
| 10 | + "github.com/smartcontractkit/chainlink-deployments-framework/datastore" |
| 11 | + cldf_deployment "github.com/smartcontractkit/chainlink-deployments-framework/deployment" |
| 12 | + capabilities_registry "github.com/smartcontractkit/chainlink-evm/gethwrappers/keystone/generated/capabilities_registry_1_1_0" |
| 13 | + "github.com/smartcontractkit/chainlink-testing-framework/framework/components/jd" |
| 14 | + |
| 15 | + "github.com/smartcontractkit/chainlink/deployment" |
| 16 | + libc "github.com/smartcontractkit/chainlink/system-tests/lib/conversions" |
| 17 | + crenode "github.com/smartcontractkit/chainlink/system-tests/lib/cre/don/node" |
| 18 | + "github.com/smartcontractkit/chainlink/system-tests/lib/cre/types" |
| 19 | +) |
| 20 | + |
| 21 | +const ( |
| 22 | + artifactDirName = "env_artifact" |
| 23 | + NOPAdminPrefix = "0xaadd000000000000000000000000000000" |
| 24 | +) |
| 25 | + |
| 26 | +type EnvArtifact struct { |
| 27 | + AddressRefs []datastore.AddressRef `json:"address_refs"` |
| 28 | + AddressBook map[uint64]map[string]cldf_deployment.TypeAndVersion `json:"address_book"` |
| 29 | + JdConfig jd.Output `json:"jd_config"` |
| 30 | + Nodes NodesArtifact `json:"nodes"` |
| 31 | + DONs []DonArtifact `json:"dons"` |
| 32 | + Bootstrappers []BootstrapNodeArtifact `json:"bootstrappers"` |
| 33 | + NOPs []NOPArtifact `json:"nops"` |
| 34 | +} |
| 35 | + |
| 36 | +type NodesArtifact struct { |
| 37 | + Nodes map[string]SimpleNodeArtifact `json:"nodes"` |
| 38 | +} |
| 39 | + |
| 40 | +type SimpleNodeArtifact struct { |
| 41 | + Name string `json:"name"` |
| 42 | +} |
| 43 | + |
| 44 | +type DonArtifact struct { |
| 45 | + DonName string `json:"don_name"` |
| 46 | + DonID int `json:"don_id"` |
| 47 | + F uint8 `json:"f"` |
| 48 | + BootstrapNodes []string `json:"bootstrap_nodes"` |
| 49 | + Capabilities []DONCapabilityArtifact `json:"capabilities,omitempty"` |
| 50 | + Nodes []FullNodeArtifact `json:"nodes"` |
| 51 | +} |
| 52 | + |
| 53 | +type FullNodeArtifact struct { |
| 54 | + Name string `json:"name"` |
| 55 | + NOP string `json:"nop"` |
| 56 | + CSAKey string `json:"csa_key"` |
| 57 | +} |
| 58 | + |
| 59 | +type DONCapabilityArtifact struct { |
| 60 | + Capability capabilities_registry.CapabilitiesRegistryCapability `json:"capability"` |
| 61 | + Config *DONCapabilityConfig `json:"config,omitempty"` |
| 62 | +} |
| 63 | + |
| 64 | +type DONCapabilityConfig struct { |
| 65 | + *capabilitiespb.CapabilityConfig |
| 66 | +} |
| 67 | + |
| 68 | +type BootstrapNodeArtifact struct { |
| 69 | + Name string `json:"name"` |
| 70 | + NOP string `json:"nop"` |
| 71 | + CSAKey string `json:"csa_key"` |
| 72 | + P2PID string `json:"p2p_id"` |
| 73 | + OCRUrl string `json:"ocr_url"` |
| 74 | + DON2DONUrl string `json:"don2d_url"` |
| 75 | +} |
| 76 | + |
| 77 | +type NOPArtifact struct { |
| 78 | + ID int `json:"id"` |
| 79 | + Name string `json:"name"` |
| 80 | + Admin string `json:"admin"` |
| 81 | +} |
| 82 | + |
| 83 | +func DumpArtifact( |
| 84 | + datastore datastore.AddressRefStore, |
| 85 | + addressBook cldf_deployment.AddressBook, |
| 86 | + jdOutput jd.Output, |
| 87 | + donTopology types.DonTopology, |
| 88 | + offchainClient cldf_deployment.OffchainClient, |
| 89 | + capabilityFactoryFns []types.DONCapabilityWithConfigFactoryFn, |
| 90 | +) error { |
| 91 | + artifact, err := GenerateArtifact(datastore, addressBook, jdOutput, donTopology, offchainClient, capabilityFactoryFns) |
| 92 | + if err != nil { |
| 93 | + return pkgerrors.Wrap(err, "failed to generate environment artifact") |
| 94 | + } |
| 95 | + |
| 96 | + // Let's save the artifact to disk |
| 97 | + return persistArtifact(artifact) |
| 98 | +} |
| 99 | + |
| 100 | +func GenerateArtifact( |
| 101 | + ds datastore.AddressRefStore, |
| 102 | + addressBook cldf_deployment.AddressBook, |
| 103 | + jdOutput jd.Output, |
| 104 | + donTopology types.DonTopology, |
| 105 | + offchainClient cldf_deployment.OffchainClient, |
| 106 | + capabilityFactoryFns []types.DONCapabilityWithConfigFactoryFn, |
| 107 | +) (*EnvArtifact, error) { |
| 108 | + var err error |
| 109 | + |
| 110 | + addresses, err := addressBook.Addresses() |
| 111 | + if err != nil { |
| 112 | + return nil, pkgerrors.Wrap(err, "failed to get addresses from address book") |
| 113 | + } |
| 114 | + |
| 115 | + addressRecords, err := ds.Fetch() |
| 116 | + if err != nil { |
| 117 | + return nil, pkgerrors.Wrap(err, "failed to fetch address records from datastore") |
| 118 | + } |
| 119 | + |
| 120 | + artifact := EnvArtifact{ |
| 121 | + JdConfig: jdOutput, |
| 122 | + AddressBook: addresses, |
| 123 | + AddressRefs: addressRecords, |
| 124 | + Nodes: NodesArtifact{ |
| 125 | + Nodes: make(map[string]SimpleNodeArtifact), |
| 126 | + }, |
| 127 | + DONs: make([]DonArtifact, 0), |
| 128 | + Bootstrappers: make([]BootstrapNodeArtifact, 0), |
| 129 | + NOPs: make([]NOPArtifact, 0), |
| 130 | + } |
| 131 | + |
| 132 | + for i, don := range donTopology.DonsWithMetadata { |
| 133 | + donArtifact := DonArtifact{ |
| 134 | + DonName: don.Name, |
| 135 | + DonID: int(don.ID), |
| 136 | + F: 0, // F will be calculated based on the number of worker nodes |
| 137 | + BootstrapNodes: make([]string, 0), |
| 138 | + Nodes: make([]FullNodeArtifact, 0), |
| 139 | + Capabilities: make([]DONCapabilityArtifact, 0), |
| 140 | + } |
| 141 | + |
| 142 | + workerNodes, workerNodesErr := crenode.FindManyWithLabel(don.NodesMetadata, &types.Label{ |
| 143 | + Key: crenode.NodeTypeKey, |
| 144 | + Value: types.WorkerNode, |
| 145 | + }, crenode.EqualLabels) |
| 146 | + if workerNodesErr != nil { |
| 147 | + return nil, pkgerrors.Wrap(workerNodesErr, "failed to find worker nodes") |
| 148 | + } |
| 149 | + |
| 150 | + donArtifact.F = libc.MustSafeUint8((len(workerNodes) - 1) / 3) |
| 151 | + |
| 152 | + for _, factoryFn := range capabilityFactoryFns { |
| 153 | + capabilities := factoryFn(don.Flags) |
| 154 | + for _, capability := range capabilities { |
| 155 | + donArtifact.Capabilities = append(donArtifact.Capabilities, DONCapabilityArtifact{ |
| 156 | + Capability: capabilities_registry.CapabilitiesRegistryCapability{ |
| 157 | + Version: capability.Capability.Version, |
| 158 | + LabelledName: capability.Capability.LabelledName, |
| 159 | + CapabilityType: capability.Capability.CapabilityType, |
| 160 | + }, |
| 161 | + Config: &DONCapabilityConfig{capability.Config}, |
| 162 | + }) |
| 163 | + } |
| 164 | + } |
| 165 | + |
| 166 | + nop := NOPArtifact{ |
| 167 | + ID: i + 1, // NOP IDs start from 1 |
| 168 | + Name: fmt.Sprintf("NOP for %s DON", don.Name), |
| 169 | + Admin: fmt.Sprintf("%s%06d", NOPAdminPrefix, i+1), |
| 170 | + } |
| 171 | + |
| 172 | + var nodeIDs []string |
| 173 | + for _, node := range don.DON.Nodes { |
| 174 | + nodeIDs = append(nodeIDs, node.NodeID) |
| 175 | + } |
| 176 | + |
| 177 | + nodeInfo, nodeInfoErr := deployment.NodeInfo(nodeIDs, offchainClient) |
| 178 | + if nodeInfoErr != nil { |
| 179 | + return nil, pkgerrors.Wrapf(nodeInfoErr, "failed to get node info for DON %s", don.Name) |
| 180 | + } |
| 181 | + |
| 182 | + for _, node := range nodeInfo { |
| 183 | + if node.IsBootstrap { |
| 184 | + donArtifact.BootstrapNodes = append(donArtifact.BootstrapNodes, node.Name) |
| 185 | + artifact.Bootstrappers = append(artifact.Bootstrappers, BootstrapNodeArtifact{ |
| 186 | + NOP: nop.Name, |
| 187 | + Name: node.Name, |
| 188 | + CSAKey: node.CSAKey, |
| 189 | + P2PID: node.PeerID.Raw(), |
| 190 | + OCRUrl: "", // TODO: this will be needed to distribute job specs |
| 191 | + DON2DONUrl: "", |
| 192 | + }) |
| 193 | + continue |
| 194 | + } |
| 195 | + |
| 196 | + artifact.Nodes.Nodes[node.NodeID] = SimpleNodeArtifact{Name: node.Name} |
| 197 | + donArtifact.Nodes = append(donArtifact.Nodes, FullNodeArtifact{ |
| 198 | + NOP: nop.Name, |
| 199 | + Name: node.Name, |
| 200 | + CSAKey: node.CSAKey, |
| 201 | + }) |
| 202 | + } |
| 203 | + |
| 204 | + artifact.NOPs = append(artifact.NOPs, nop) |
| 205 | + artifact.DONs = append(artifact.DONs, donArtifact) |
| 206 | + } |
| 207 | + |
| 208 | + // Let's save the artifact to disk |
| 209 | + if err = persistArtifact(&artifact); err != nil { |
| 210 | + return nil, pkgerrors.Wrap(err, "failed to persist environment artifact") |
| 211 | + } |
| 212 | + |
| 213 | + return &artifact, nil |
| 214 | +} |
| 215 | + |
| 216 | +func persistArtifact(artifact *EnvArtifact) error { |
| 217 | + err := os.MkdirAll(artifactDirName, 0755) |
| 218 | + if err != nil { |
| 219 | + return pkgerrors.Wrap(err, "failed to create directory for the environment artifact") |
| 220 | + } |
| 221 | + err = WriteJSONFile(artifactDirName+"/env_artifact.json", artifact) |
| 222 | + if err != nil { |
| 223 | + return pkgerrors.Wrap(err, "failed to write environment artifact to file") |
| 224 | + } |
| 225 | + |
| 226 | + return nil |
| 227 | +} |
0 commit comments