diff --git a/grid-cli/cmd/deploy_vm.go b/grid-cli/cmd/deploy_vm.go index e72d8afb0..f22b58d26 100644 --- a/grid-cli/cmd/deploy_vm.go +++ b/grid-cli/cmd/deploy_vm.go @@ -12,7 +12,7 @@ import ( "github.com/threefoldtech/tfgrid-sdk-go/grid-cli/internal/filters" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/deployer" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" - "github.com/threefoldtech/zos/pkg/gridtypes/zos" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) var ( diff --git a/grid-cli/go.mod b/grid-cli/go.mod index e5e149e29..a8672f3cf 100644 --- a/grid-cli/go.mod +++ b/grid-cli/go.mod @@ -47,6 +47,7 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20240827163226-d4e15e206974 // indirect github.com/threefoldtech/tfgrid-sdk-go/rmb-sdk-go v0.15.18 // indirect + github.com/threefoldtech/zos4 v0.5.6-0.20241008102757-02d898c580c4 // indirect golang.org/x/crypto v0.27.0 // indirect golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect golang.org/x/sync v0.8.0 // indirect diff --git a/grid-cli/go.sum b/grid-cli/go.sum index 167066f45..365d95348 100644 --- a/grid-cli/go.sum +++ b/grid-cli/go.sum @@ -118,6 +118,8 @@ github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20240827163226 github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20240827163226-d4e15e206974/go.mod h1:dtDKAPiUDxAwIkfHV7xcAFZcOm+xwNIuOI1MLFS+MeQ= github.com/threefoldtech/zos v0.5.6-0.20240902110349-172a0a29a6ee h1:pqpYVM0qkXujplHNfH6w5GDqcY5sLJAgOc4/hlR6+Xw= github.com/threefoldtech/zos v0.5.6-0.20240902110349-172a0a29a6ee/go.mod h1:lut72yYMJhgK0QRvF0Wd/mB3+OfIoXWz04DQuXck3Sw= +github.com/threefoldtech/zos4 v0.5.6-0.20241008102757-02d898c580c4 h1:JCExxpPL32G7evO/+gHwlZLfAX1+l9QN9t55tnPDCp0= +github.com/threefoldtech/zos4 v0.5.6-0.20241008102757-02d898c580c4/go.mod h1:7KFtZaCcEFwQ1/cz/+hkYK616Ww04ISZgmMqLWHz6To= github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= diff --git a/grid-cli/internal/cmd/deploy.go b/grid-cli/internal/cmd/deploy.go index 5da640919..dffb36df9 100644 --- a/grid-cli/internal/cmd/deploy.go +++ b/grid-cli/internal/cmd/deploy.go @@ -11,7 +11,7 @@ import ( "github.com/rs/zerolog/log" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/deployer" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" - "github.com/threefoldtech/zos/pkg/gridtypes" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) // DeployVM deploys a vm with mounts @@ -32,7 +32,7 @@ func DeployVM(ctx context.Context, t deployer.TFPluginClient, vm workloads.VM, d volumeMounts = append(volumeMounts, volumeMount) } vm.NetworkName = networkName - dl := workloads.NewDeployment(vm.Name, vm.NodeID, projectName, nil, networkName, diskMounts, nil, []workloads.VM{vm}, nil, volumeMounts) + dl := workloads.NewDeployment(vm.Name, vm.NodeID, projectName, nil, networkName, diskMounts, nil, []workloads.VM{vm}, nil, nil, volumeMounts) log.Info().Msg("deploying network") err = t.NetworkDeployer.Deploy(ctx, &network) @@ -138,7 +138,7 @@ func DeployGatewayFQDN(ctx context.Context, t deployer.TFPluginClient, gateway w // DeployZDBs deploys multiple zdbs func DeployZDBs(ctx context.Context, t deployer.TFPluginClient, projectName string, zdbs []workloads.ZDB, n int, node uint32) ([]workloads.ZDB, error) { - dl := workloads.NewDeployment(projectName, node, projectName, nil, "", nil, zdbs, nil, nil, nil) + dl := workloads.NewDeployment(projectName, node, projectName, nil, "", nil, zdbs, nil, nil, nil, nil) log.Info().Msgf("deploying zdbs") err := t.DeploymentDeployer.Deploy(ctx, &dl) if err != nil { @@ -172,10 +172,10 @@ func buildNetwork(name, projectName string, nodes []uint32, addMycelium bool) (w return workloads.ZNet{ Name: name, Nodes: nodes, - IPRange: gridtypes.NewIPNet(net.IPNet{ + IPRange: zos.IPNet{IPNet: net.IPNet{ IP: net.IPv4(10, 20, 0, 0), Mask: net.CIDRMask(16, 32), - }), + }}, MyceliumKeys: keys, SolutionType: projectName, }, nil diff --git a/grid-client/deployer/deployer.go b/grid-client/deployer/deployer.go index d51087505..9cf3c2dde 100644 --- a/grid-client/deployer/deployer.go +++ b/grid-client/deployer/deployer.go @@ -16,10 +16,10 @@ import ( substrate "github.com/threefoldtech/tfchain/clients/tfchain-client-go" client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/subi" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" proxy "github.com/threefoldtech/tfgrid-sdk-go/grid-proxy/pkg/client" proxyTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-proxy/pkg/types" "github.com/threefoldtech/zos/pkg/gridtypes" - "github.com/threefoldtech/zos/pkg/gridtypes/zos" "golang.org/x/sync/errgroup" ) @@ -27,7 +27,7 @@ import ( type MockDeployer interface { // TODO: Change Name && separate them Deploy(ctx context.Context, oldDeploymentIDs map[uint32]uint64, - newDeployments map[uint32]gridtypes.Deployment, + newDeployments map[uint32]zos.Deployment, newDeploymentSolutionProvider map[uint32]*uint64, ) (map[uint32]uint64, error) @@ -35,11 +35,11 @@ type MockDeployer interface { // TODO: Change Name && separate them contractID uint64, ) error - GetDeployments(ctx context.Context, dls map[uint32]uint64) (map[uint32]gridtypes.Deployment, error) + GetDeployments(ctx context.Context, dls map[uint32]uint64) (map[uint32]zos.Deployment, error) BatchDeploy(ctx context.Context, - deployments map[uint32][]gridtypes.Deployment, + deployments map[uint32][]zos.Deployment, deploymentsSolutionProvider map[uint32][]*uint64, - ) (map[uint32][]gridtypes.Deployment, error) + ) (map[uint32][]zos.Deployment, error) } // Deployer to be used for any deployer @@ -70,7 +70,7 @@ func NewDeployer( // Deploy deploys or updates a new deployment given the old deployments' IDs func (d *Deployer) Deploy(ctx context.Context, oldDeploymentIDs map[uint32]uint64, - newDeployments map[uint32]gridtypes.Deployment, + newDeployments map[uint32]zos.Deployment, newDeploymentSolutionProvider map[uint32]*uint64, ) (map[uint32]uint64, error) { oldDeployments, oldErr := d.GetDeployments(ctx, oldDeploymentIDs) @@ -104,7 +104,7 @@ func (d *Deployer) Deploy(ctx context.Context, func (d *Deployer) deploy( ctx context.Context, oldDeployments map[uint32]uint64, - newDeployments map[uint32]gridtypes.Deployment, + newDeployments map[uint32]zos.Deployment, newDeploymentSolutionProvider map[uint32]*uint64, revertOnFailure bool, ) (currentDeployments map[uint32]uint64, err error) { @@ -173,7 +173,7 @@ func (d *Deployer) deploy( currentDeployments[node] = dl.ContractID newWorkloadVersions := make(map[string]uint32) for _, w := range dl.Workloads { - newWorkloadVersions[w.Name.String()] = 0 + newWorkloadVersions[w.Name] = 0 } err = d.Wait(ctx, client, dl.ContractID, newWorkloadVersions) if err != nil { @@ -271,8 +271,8 @@ func (d *Deployer) Cancel(ctx context.Context, } // GetDeployments returns deployments from a map of nodes IDs and deployments IDs -func (d *Deployer) GetDeployments(ctx context.Context, dls map[uint32]uint64) (map[uint32]gridtypes.Deployment, error) { - res := make(map[uint32]gridtypes.Deployment) +func (d *Deployer) GetDeployments(ctx context.Context, dls map[uint32]uint64) (map[uint32]zos.Deployment, error) { + res := make(map[uint32]zos.Deployment) for nodeID, dlID := range dls { nc, err := d.ncPool.GetNodeClient(d.substrateConn, nodeID) @@ -284,6 +284,7 @@ func (d *Deployer) GetDeployments(ctx context.Context, dls map[uint32]uint64) (m if err != nil { return nil, errors.Wrapf(err, "failed to get deployment %d of node %d", dlID, nodeID) } + res[nodeID] = dl } @@ -324,18 +325,18 @@ func (d *Deployer) Wait( } for _, wl := range deploymentChanges { - if _, ok := workloadVersions[wl.Name.String()]; ok && wl.Version == workloadVersions[wl.Name.String()] { + if _, ok := workloadVersions[wl.Name]; ok && wl.Version == workloadVersions[wl.Name] { var errString string switch wl.Result.State { - case gridtypes.StateOk: + case zos.StateOk: stateOk++ - case gridtypes.StateError: + case zos.StateError: errString = fmt.Sprintf("workload %s within deployment %d failed with error: %s", wl.Name, deploymentID, wl.Result.Error) - case gridtypes.StateDeleted: + case zos.StateDeleted: errString = fmt.Sprintf("workload %s state within deployment %d is deleted: %s", wl.Name, deploymentID, wl.Result.Error) - case gridtypes.StatePaused: + case zos.StatePaused: errString = fmt.Sprintf("workload %s state within deployment %d is paused: %s", wl.Name, deploymentID, wl.Result.Error) - case gridtypes.StateUnChanged: + case zos.StateUnChanged: errString = fmt.Sprintf("workload %s within deployment %d was not updated: %s", wl.Name, deploymentID, wl.Result.Error) } if errString != "" { @@ -364,8 +365,12 @@ func (d *Deployer) Wait( } // BatchDeploy deploys a batch of deployments, successful deployments should have ContractID fields set -func (d *Deployer) BatchDeploy(ctx context.Context, deployments map[uint32][]gridtypes.Deployment, deploymentsSolutionProvider map[uint32][]*uint64) (map[uint32][]gridtypes.Deployment, error) { - deploymentsSlice := make([]gridtypes.Deployment, 0) +func (d *Deployer) BatchDeploy( + ctx context.Context, + deployments map[uint32][]zos.Deployment, + deploymentsSolutionProvider map[uint32][]*uint64, +) (map[uint32][]zos.Deployment, error) { + deploymentsSlice := make([]zos.Deployment, 0) contractsData := make([]substrate.BatchCreateContractData, 0) mu := sync.Mutex{} @@ -375,7 +380,7 @@ func (d *Deployer) BatchDeploy(ctx context.Context, deployments map[uint32][]gri // loading node clients first before creating any contract and caching the clients _, err := d.ncPool.GetNodeClient(d.substrateConn, node) if err != nil { - return map[uint32][]gridtypes.Deployment{}, errors.Wrap(err, "failed to get node client") + return map[uint32][]zos.Deployment{}, errors.Wrap(err, "failed to get node client") } for i, dl := range dls { i := i @@ -432,12 +437,12 @@ func (d *Deployer) BatchDeploy(ctx context.Context, deployments map[uint32][]gri } if err := group.Wait(); err != nil { - return map[uint32][]gridtypes.Deployment{}, err + return map[uint32][]zos.Deployment{}, err } contracts, index, err := d.substrateConn.BatchCreateContract(d.identity, contractsData) if err != nil && index == nil { - return map[uint32][]gridtypes.Deployment{}, errors.Wrap(err, "failed to create contracts") + return map[uint32][]zos.Deployment{}, errors.Wrap(err, "failed to create contracts") } var multiErr error @@ -477,7 +482,7 @@ func (d *Deployer) BatchDeploy(ctx context.Context, deployments map[uint32][]gri } newWorkloadVersions := make(map[string]uint32) for _, w := range dl.Workloads { - newWorkloadVersions[w.Name.String()] = 0 + newWorkloadVersions[w.Name] = 0 } err = d.Wait(ctx, client, dl.ContractID, newWorkloadVersions) mu.Lock() @@ -492,7 +497,7 @@ func (d *Deployer) BatchDeploy(ctx context.Context, deployments map[uint32][]gri } wg.Wait() - resDeployments := make(map[uint32][]gridtypes.Deployment, len(deployments)) + resDeployments := make(map[uint32][]zos.Deployment, len(deployments)) for i, dl := range deploymentsSlice { resDeployments[contractsData[i].Node] = append(resDeployments[contractsData[i].Node], dl) } @@ -508,21 +513,21 @@ func (d *Deployer) BatchDeploy(ctx context.Context, deployments map[uint32][]gri } // matchOldVersions assigns deployment and workloads versions of the new versionless deployment to the ones of the old deployment -func matchOldVersions(oldDl *gridtypes.Deployment, newDl *gridtypes.Deployment) { +func matchOldVersions(oldDl *zos.Deployment, newDl *zos.Deployment) { oldWlVersions := map[string]uint32{} for _, wl := range oldDl.Workloads { - oldWlVersions[wl.Name.String()] = wl.Version + oldWlVersions[wl.Name] = wl.Version } newDl.Version = oldDl.Version for idx, wl := range newDl.Workloads { - newDl.Workloads[idx].Version = oldWlVersions[wl.Name.String()] + newDl.Workloads[idx].Version = oldWlVersions[wl.Name] } } // assignVersions determines and assigns the versions of the new deployment and its workloads -func assignVersions(oldDl *gridtypes.Deployment, newDl *gridtypes.Deployment) (map[string]uint32, error) { +func assignVersions(oldDl *zos.Deployment, newDl *zos.Deployment) (map[string]uint32, error) { oldHashes, err := GetWorkloadHashes(*oldDl) if err != nil { return nil, errors.Wrap(err, "could not get old workloads hashes") @@ -537,12 +542,12 @@ func assignVersions(oldDl *gridtypes.Deployment, newDl *gridtypes.Deployment) (m newDl.Version = oldDl.Version + 1 for idx, w := range newDl.Workloads { - newHash := newHashes[string(w.Name)] - oldHash, ok := oldHashes[string(w.Name)] + newHash := newHashes[w.Name] + oldHash, ok := oldHashes[w.Name] if !ok || newHash != oldHash { newDl.Workloads[idx].Version = newDl.Version } - newWorkloadsVersions[w.Name.String()] = newDl.Workloads[idx].Version + newWorkloadsVersions[w.Name] = newDl.Workloads[idx].Version } return newWorkloadsVersions, nil @@ -558,7 +563,7 @@ func assignVersions(oldDl *gridtypes.Deployment, newDl *gridtypes.Deployment) (m // // errors that may arise because of dead nodes are ignored. // if a real error dodges the validation, it'll be fail anyway in the deploying phase -func (d *Deployer) Validate(ctx context.Context, oldDeployments map[uint32]gridtypes.Deployment, newDeployments map[uint32]gridtypes.Deployment) error { +func (d *Deployer) Validate(ctx context.Context, oldDeployments map[uint32]zos.Deployment, newDeployments map[uint32]zos.Deployment) error { farmIPs := make(map[int]int) nodeMap := make(map[uint32]proxyTypes.NodeWithNestedCapacity) @@ -619,11 +624,12 @@ func (d *Deployer) Validate(ctx context.Context, oldDeployments map[uint32]gridt } for node, dl := range newDeployments { - oldDl, alreadyExists := oldDeployments[node] if err := dl.Valid(); err != nil { return errors.Wrap(err, "invalid deployment") } + oldDl, alreadyExists := oldDeployments[node] + needed, err := Capacity(dl) if err != nil { return err @@ -669,13 +675,13 @@ func (d *Deployer) Validate(ctx context.Context, oldDeployments map[uint32]gridt mru := nodeInfo.Capacity.Total.MRU - nodeInfo.Capacity.Used.MRU hru := nodeInfo.Capacity.Total.HRU - nodeInfo.Capacity.Used.HRU sru := 2*nodeInfo.Capacity.Total.SRU - nodeInfo.Capacity.Used.SRU - if mru < needed.MRU || - sru < needed.SRU || - hru < needed.HRU { - free := gridtypes.Capacity{ - HRU: hru, - MRU: mru, - SRU: sru, + if uint64(mru) < needed.MRU || + uint64(sru) < needed.SRU || + uint64(hru) < needed.HRU { + free := zos.Capacity{ + HRU: uint64(hru), + MRU: uint64(mru), + SRU: uint64(sru), } return errors.Errorf("node %d does not have enough resources. needed: %v, free: %v", node, capacityPrettyPrint(needed), capacityPrettyPrint(free)) } @@ -684,14 +690,14 @@ func (d *Deployer) Validate(ctx context.Context, oldDeployments map[uint32]gridt } // capacityPrettyPrint prints the capacity data -func capacityPrettyPrint(cap gridtypes.Capacity) string { +func capacityPrettyPrint(cap zos.Capacity) string { return fmt.Sprintf("[mru: %d, sru: %d, hru: %d]", cap.MRU, cap.SRU, cap.HRU) } // addCapacity adds a new data for capacity -func addCapacity(cap *proxyTypes.Capacity, add *gridtypes.Capacity) { +func addCapacity(cap *proxyTypes.Capacity, add *zos.Capacity) { cap.CRU += add.CRU - cap.MRU += add.MRU - cap.SRU += add.SRU - cap.HRU += add.HRU + cap.MRU += gridtypes.Unit(add.MRU) + cap.SRU += gridtypes.Unit(add.SRU) + cap.HRU += gridtypes.Unit(add.HRU) } diff --git a/grid-client/deployer/deployer_test.go b/grid-client/deployer/deployer_test.go index 1385eb738..8af820adc 100644 --- a/grid-client/deployer/deployer_test.go +++ b/grid-client/deployer/deployer_test.go @@ -16,6 +16,7 @@ import ( client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/subi" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" proxyTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-proxy/pkg/types" "github.com/threefoldtech/zos/pkg/gridtypes" "github.com/threefoldtech/zos/pkg/gridtypes/zos" @@ -58,22 +59,22 @@ type gatewayWorkloadGenerator interface { ZosWorkload() gridtypes.Workload } -func newDeploymentWithGateway(identity substrate.Identity, twinID uint32, version uint32, gw gatewayWorkloadGenerator) (gridtypes.Deployment, error) { - dl := workloads.NewGridDeployment(twinID, []gridtypes.Workload{}) +func newDeploymentWithGateway(identity substrate.Identity, twinID uint32, version uint32, gw gatewayWorkloadGenerator) (zosTypes.Deployment, error) { + dl := workloads.NewGridDeployment(twinID, 0, []zosTypes.Workload{}) dl.Version = version - dl.Workloads = append(dl.Workloads, gw.ZosWorkload()) + dl.Workloads = append(dl.Workloads, zosTypes.NewWorkloadFromZosWorkload(gw.ZosWorkload())) dl.Workloads[0].Version = version err := dl.Sign(twinID, identity) if err != nil { - return gridtypes.Deployment{}, err + return zosTypes.Deployment{}, err } return dl, nil } -func deploymentWithNameGateway(identity substrate.Identity, twinID uint32, TLSPassthrough bool, version uint32, backendURL string) (gridtypes.Deployment, error) { +func deploymentWithNameGateway(identity substrate.Identity, twinID uint32, TLSPassthrough bool, version uint32, backendURL string) (zosTypes.Deployment, error) { gw := workloads.GatewayNameProxy{ Name: "name", TLSPassthrough: TLSPassthrough, @@ -83,7 +84,7 @@ func deploymentWithNameGateway(identity substrate.Identity, twinID uint32, TLSPa return newDeploymentWithGateway(identity, twinID, version, &gw) } -func deploymentWithFQDN(identity substrate.Identity, twinID uint32, version uint32) (gridtypes.Deployment, error) { +func deploymentWithFQDN(identity substrate.Identity, twinID uint32, version uint32) (zosTypes.Deployment, error) { gw := workloads.GatewayFQDNProxy{ Name: "fqdn", FQDN: "a.b.com", @@ -93,7 +94,7 @@ func deploymentWithFQDN(identity substrate.Identity, twinID uint32, version uint return newDeploymentWithGateway(identity, twinID, version, &gw) } -func hash(dl *gridtypes.Deployment) (string, error) { +func hash(dl *zosTypes.Deployment) (string, error) { hash, err := dl.ChallengeHash() if err != nil { return "", err @@ -152,7 +153,7 @@ func TestDeployer(t *testing.T) { dl2, err := deploymentWithFQDN(identity, twinID, 0) require.NoError(t, err) - newDls := map[uint32]gridtypes.Deployment{ + newDls := map[uint32]zosTypes.Deployment{ 10: dl1, 20: dl2, } @@ -201,17 +202,17 @@ func TestDeployer(t *testing.T) { Return(client.NewNodeClient(23, cl, tfPluginClient.RMBTimeout), nil) cl.EXPECT(). - Call(gomock.Any(), uint32(13), "zos.deployment.deploy", dl1, gomock.Any()). + Call(gomock.Any(), uint32(13), "zos.deployment.deploy", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - dl1.Workloads[0].Result.State = gridtypes.StateOk + dl1.Workloads[0].Result.State = zosTypes.StateOk dl1.Workloads[0].Result.Data, _ = json.Marshal(zos.GatewayProxyResult{}) return nil }) cl.EXPECT(). - Call(gomock.Any(), uint32(23), "zos.deployment.deploy", dl2, gomock.Any()). + Call(gomock.Any(), uint32(23), "zos.deployment.deploy", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - dl2.Workloads[0].Result.State = gridtypes.StateOk + dl2.Workloads[0].Result.State = zosTypes.StateOk dl2.Workloads[0].Result.Data, _ = json.Marshal(zos.GatewayFQDNResult{}) return nil }) @@ -219,7 +220,7 @@ func TestDeployer(t *testing.T) { cl.EXPECT(). Call(gomock.Any(), uint32(13), "zos.deployment.changes", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - var res *[]gridtypes.Workload = result.(*[]gridtypes.Workload) + var res *[]zosTypes.Workload = result.(*[]zosTypes.Workload) *res = dl1.Workloads return nil }) @@ -227,7 +228,7 @@ func TestDeployer(t *testing.T) { cl.EXPECT(). Call(gomock.Any(), uint32(23), "zos.deployment.changes", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - var res *[]gridtypes.Workload = result.(*[]gridtypes.Workload) + var res *[]zosTypes.Workload = result.(*[]zosTypes.Workload) *res = dl2.Workloads return nil }) @@ -246,7 +247,7 @@ func TestDeployer(t *testing.T) { assert.NoError(t, err) versionedDl.SignatureRequirement = newVersionlessDl.SignatureRequirement - newDls := map[uint32]gridtypes.Deployment{ + newDls := map[uint32]zosTypes.Deployment{ 10: newVersionlessDl, } @@ -273,7 +274,7 @@ func TestDeployer(t *testing.T) { cl.EXPECT(). Call(gomock.Any(), uint32(13), "zos.deployment.get", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - var res *gridtypes.Deployment = result.(*gridtypes.Deployment) + var res *zosTypes.Deployment = result.(*zosTypes.Deployment) *res = oldDl return nil }).AnyTimes() @@ -290,9 +291,9 @@ func TestDeployer(t *testing.T) { ).Return(uint64(100), nil) cl.EXPECT(). - Call(gomock.Any(), uint32(13), "zos.deployment.update", versionedDl, gomock.Any()). + Call(gomock.Any(), uint32(13), "zos.deployment.update", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - versionedDl.Workloads[0].Result.State = gridtypes.StateOk + versionedDl.Workloads[0].Result.State = zosTypes.StateOk versionedDl.Workloads[0].Result.Data, _ = json.Marshal(zos.GatewayProxyResult{}) return nil }) @@ -300,7 +301,7 @@ func TestDeployer(t *testing.T) { cl.EXPECT(). Call(gomock.Any(), uint32(13), "zos.deployment.changes", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - var res *[]gridtypes.Workload = result.(*[]gridtypes.Workload) + var res *[]zosTypes.Workload = result.(*[]zosTypes.Workload) *res = versionedDl.Workloads return nil }).AnyTimes() @@ -347,8 +348,8 @@ func TestDeployer(t *testing.T) { dl6, err := deploymentWithNameGateway(identity, twinID, true, 0, backendURLWithTLSPassthrough) assert.NoError(t, err) - dl2.Workloads = append(dl2.Workloads, g.ZosWorkload()) - dl3.Workloads = append(dl3.Workloads, g.ZosWorkload()) + dl2.Workloads = append(dl2.Workloads, zosTypes.NewWorkloadFromZosWorkload(g.ZosWorkload())) + dl3.Workloads = append(dl3.Workloads, zosTypes.NewWorkloadFromZosWorkload(g.ZosWorkload())) assert.NoError(t, dl2.Sign(twinID, identity)) assert.NoError(t, dl3.Sign(twinID, identity)) @@ -367,7 +368,7 @@ func TestDeployer(t *testing.T) { 20: 200, 40: 400, } - newDls := map[uint32]gridtypes.Deployment{ + newDls := map[uint32]zosTypes.Deployment{ 20: dl3, 30: dl4, 40: dl6, @@ -427,7 +428,7 @@ func TestDeployer(t *testing.T) { cl.EXPECT(). Call(gomock.Any(), uint32(13), "zos.deployment.changes", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - var res *[]gridtypes.Workload = result.(*[]gridtypes.Workload) + var res *[]zosTypes.Workload = result.(*[]zosTypes.Workload) *res = dl1.Workloads return nil }).AnyTimes() @@ -435,7 +436,7 @@ func TestDeployer(t *testing.T) { cl.EXPECT(). Call(gomock.Any(), uint32(23), "zos.deployment.changes", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - var res *[]gridtypes.Workload = result.(*[]gridtypes.Workload) + var res *[]zosTypes.Workload = result.(*[]zosTypes.Workload) *res = dl2.Workloads return nil }).AnyTimes() @@ -443,7 +444,7 @@ func TestDeployer(t *testing.T) { cl.EXPECT(). Call(gomock.Any(), uint32(33), "zos.deployment.changes", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - var res *[]gridtypes.Workload = result.(*[]gridtypes.Workload) + var res *[]zosTypes.Workload = result.(*[]zosTypes.Workload) *res = dl4.Workloads return nil }).AnyTimes() @@ -451,7 +452,7 @@ func TestDeployer(t *testing.T) { cl.EXPECT(). Call(gomock.Any(), uint32(43), "zos.deployment.changes", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - var res *[]gridtypes.Workload = result.(*[]gridtypes.Workload) + var res *[]zosTypes.Workload = result.(*[]zosTypes.Workload) *res = dl5.Workloads return nil }).AnyTimes() @@ -459,7 +460,7 @@ func TestDeployer(t *testing.T) { cl.EXPECT(). Call(gomock.Any(), uint32(13), "zos.deployment.get", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - var res *gridtypes.Deployment = result.(*gridtypes.Deployment) + var res *zosTypes.Deployment = result.(*zosTypes.Deployment) *res = dl1 return nil }).AnyTimes() @@ -467,7 +468,7 @@ func TestDeployer(t *testing.T) { cl.EXPECT(). Call(gomock.Any(), uint32(23), "zos.deployment.get", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - var res *gridtypes.Deployment = result.(*gridtypes.Deployment) + var res *zosTypes.Deployment = result.(*zosTypes.Deployment) *res = dl2 return nil }).AnyTimes() @@ -475,7 +476,7 @@ func TestDeployer(t *testing.T) { cl.EXPECT(). Call(gomock.Any(), uint32(33), "zos.deployment.get", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - var res *gridtypes.Deployment = result.(*gridtypes.Deployment) + var res *zosTypes.Deployment = result.(*zosTypes.Deployment) *res = dl4 return nil }).AnyTimes() @@ -483,7 +484,7 @@ func TestDeployer(t *testing.T) { cl.EXPECT(). Call(gomock.Any(), uint32(43), "zos.deployment.get", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - var res *gridtypes.Deployment = result.(*gridtypes.Deployment) + var res *zosTypes.Deployment = result.(*zosTypes.Deployment) *res = dl5 return nil }).AnyTimes() @@ -494,9 +495,9 @@ func TestDeployer(t *testing.T) { dl2.Workloads = dl3.Workloads dl2.Version = 1 dl2.Workloads[0].Version = 1 - dl2.Workloads[0].Result.State = gridtypes.StateOk + dl2.Workloads[0].Result.State = zosTypes.StateOk dl2.Workloads[0].Result.Data, _ = json.Marshal(zos.GatewayProxyResult{}) - dl2.Workloads[1].Result.State = gridtypes.StateOk + dl2.Workloads[1].Result.State = zosTypes.StateOk dl2.Workloads[1].Result.Data, _ = json.Marshal(zos.GatewayProxyResult{}) return nil }) @@ -504,7 +505,7 @@ func TestDeployer(t *testing.T) { cl.EXPECT(). Call(gomock.Any(), uint32(33), "zos.deployment.deploy", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - dl4.Workloads[0].Result.State = gridtypes.StateOk + dl4.Workloads[0].Result.State = zosTypes.StateOk dl4.Workloads[0].Result.Data, _ = json.Marshal(zos.GatewayProxyResult{}) return nil }) diff --git a/grid-client/deployer/deployment_deployer.go b/grid-client/deployer/deployment_deployer.go index b0c6c0786..716e4fc73 100644 --- a/grid-client/deployer/deployment_deployer.go +++ b/grid-client/deployer/deployment_deployer.go @@ -4,15 +4,13 @@ import ( "context" "fmt" "net" - "slices" "sync" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" - "github.com/threefoldtech/zos/pkg/gridtypes" - "github.com/threefoldtech/zos/pkg/gridtypes/zos" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) // DeploymentDeployer for deploying a deployment @@ -46,8 +44,8 @@ func (d *DeploymentDeployer) Validate(ctx context.Context, dls []*workloads.Depl } // GenerateVersionlessDeployments generates a new deployment without a version -func (d *DeploymentDeployer) GenerateVersionlessDeployments(ctx context.Context, dls []*workloads.Deployment) (map[uint32][]gridtypes.Deployment, error) { - gridDlsPerNodes := make(map[uint32][]gridtypes.Deployment) +func (d *DeploymentDeployer) GenerateVersionlessDeployments(ctx context.Context, dls []*workloads.Deployment) (map[uint32][]zos.Deployment, error) { + gridDlsPerNodes := make(map[uint32][]zos.Deployment) var wg sync.WaitGroup var mu sync.Mutex @@ -62,7 +60,7 @@ func (d *DeploymentDeployer) GenerateVersionlessDeployments(ctx context.Context, wg.Add(1) go func(dl *workloads.Deployment) { defer wg.Done() - newDl := workloads.NewGridDeployment(d.tfPluginClient.TwinID, []gridtypes.Workload{}) + newDl := workloads.NewGridDeployment(d.tfPluginClient.TwinID, 0, []zos.Workload{}) for _, disk := range dl.Disks { newDl.Workloads = append(newDl.Workloads, disk.ZosWorkload()) } @@ -75,6 +73,9 @@ func (d *DeploymentDeployer) GenerateVersionlessDeployments(ctx context.Context, for _, vm := range dl.Vms { newDl.Workloads = append(newDl.Workloads, vm.ZosWorkload()...) } + for _, vm := range dl.VmsLight { + newDl.Workloads = append(newDl.Workloads, vm.ZosWorkload()...) + } for idx, q := range dl.QSFS { qsfsWorkload, err := q.ZosWorkload() @@ -120,7 +121,7 @@ func (d *DeploymentDeployer) Deploy(ctx context.Context, dl *workloads.Deploymen dl.NodeDeploymentID, err = d.deployer.Deploy( ctx, dl.NodeDeploymentID, - map[uint32]gridtypes.Deployment{dl.NodeID: dlsPerNodes[dl.NodeID][0]}, + map[uint32]zos.Deployment{dl.NodeID: dlsPerNodes[dl.NodeID][0]}, map[uint32]*uint64{dl.NodeID: dl.SolutionProvider}, ) @@ -160,7 +161,7 @@ func (d *DeploymentDeployer) BatchDeploy(ctx context.Context, dls []*workloads.D // update deployment and plugin state // error is not returned immediately before updating state because of untracked failed deployments for _, dl := range dls { - if err := d.updateStateFromDeployments(ctx, dl, newDls); err != nil { + if err := d.updateStateFromDeployments(dl, newDls); err != nil { multiErr = multierror.Append(multiErr, errors.Wrapf(err, "failed to update deployment '%s' state", dl.Name)) } } @@ -187,7 +188,7 @@ func (d *DeploymentDeployer) Cancel(ctx context.Context, dl *workloads.Deploymen return nil } -func (d *DeploymentDeployer) updateStateFromDeployments(ctx context.Context, dl *workloads.Deployment, newDls map[uint32][]gridtypes.Deployment) error { +func (d *DeploymentDeployer) updateStateFromDeployments(dl *workloads.Deployment, newDls map[uint32][]zos.Deployment) error { dl.NodeDeploymentID = map[uint32]uint64{} for _, newDl := range newDls[dl.NodeID] { @@ -257,7 +258,7 @@ func (d *DeploymentDeployer) calculateNetworksUsedIPs(ctx context.Context, dls [ // calculate used host IDs per network for _, dl := range dls { - if len(dl.Vms) == 0 { + if len(dl.Vms) == 0 && len(dl.VmsLight) == 0 { continue } @@ -303,7 +304,7 @@ func (d *DeploymentDeployer) assignPrivateIPs(ctx context.Context, dls []*worklo } for _, dl := range dls { - if len(dl.Vms) == 0 { + if len(dl.Vms) == 0 && len(dl.VmsLight) == 0 { newDls = append(newDls, dl) continue } @@ -320,45 +321,27 @@ func (d *DeploymentDeployer) assignPrivateIPs(ctx context.Context, dls []*worklo curHostID := byte(2) for idx, vm := range dl.Vms { - vmIP := net.ParseIP(vm.IP).To4() - - // if vm private ip is given - if vmIP != nil { - vmHostID := vmIP[3] // host ID of the private ip - - nodeUsedHostIDs := usedHosts[dl.NetworkName][dl.NodeID] - - // TODO: use of a duplicate IP vs an updated vm with a new/old IP - if slices.Contains(nodeUsedHostIDs, vmHostID) { - continue - // return fmt.Errorf("duplicate private ip '%v' in vm '%s' is used", vmIP, vm.Name) - } - - if !ipRangeCIDR.Contains(vmIP) { - errs = multierror.Append(errs, fmt.Errorf("deployment ip range '%v' doesn't contain ip '%v' for vm '%s'", ipRange, vmIP, vm.Name)) - continue - } - - usedHosts[dl.NetworkName][dl.NodeID] = append(usedHosts[dl.NetworkName][dl.NodeID], vmHostID) + vmIP, err := vm.AssignPrivateIP(dl.NetworkName, ipRange, dl.NodeID, + ipRangeCIDR, ip, curHostID, usedHosts, + ) + if err != nil { + errs = multierror.Append(errs, errors.Wrapf(err, "failed to assign IP to vm %s", vm.Name)) continue } - nodeUsedHostIDs := usedHosts[dl.NetworkName][dl.NodeID] + dl.Vms[idx].IP = vmIP + } - // try to find available host ID in the deployment ip range - for slices.Contains(nodeUsedHostIDs, curHostID) { - if curHostID == 254 { - errs = multierror.Append(errs, errors.New("all 253 ips of the network are exhausted")) - continue - } - curHostID++ + for idx, vmLight := range dl.VmsLight { + vmLightIP, err := vmLight.AssignPrivateIP(dl.NetworkName, ipRange, dl.NodeID, + ipRangeCIDR, ip, curHostID, usedHosts, + ) + if err != nil { + errs = multierror.Append(errs, errors.Wrapf(err, "failed to assign IP to vm %s", vmLight.Name)) + continue } - usedHosts[dl.NetworkName][dl.NodeID] = append(usedHosts[dl.NetworkName][dl.NodeID], curHostID) - - vmIP = ip.To4() - vmIP[3] = curHostID - dl.Vms[idx].IP = vmIP.String() + dl.VmsLight[idx].IP = vmLightIP } dl.IPrange = ipRange @@ -368,7 +351,7 @@ func (d *DeploymentDeployer) assignPrivateIPs(ctx context.Context, dls []*worklo return newDls, errs } -func (d *DeploymentDeployer) syncContract(ctx context.Context, dl *workloads.Deployment) error { +func (d *DeploymentDeployer) syncContract(dl *workloads.Deployment) error { sub := d.tfPluginClient.SubstrateConn if dl.ContractID == 0 { @@ -389,7 +372,7 @@ func (d *DeploymentDeployer) syncContract(ctx context.Context, dl *workloads.Dep // Sync syncs the deployments // TODO: remove func (d *DeploymentDeployer) Sync(ctx context.Context, dl *workloads.Deployment) error { - err := d.syncContract(ctx, dl) + err := d.syncContract(dl) if err != nil { return err } @@ -406,6 +389,7 @@ func (d *DeploymentDeployer) Sync(ctx context.Context, dl *workloads.Deployment) } vms := make([]workloads.VM, 0) + vmsLight := make([]workloads.VMLight, 0) zdbs := make([]workloads.ZDB, 0) qsfs := make([]workloads.QSFS, 0) disks := make([]workloads.Disk, 0) @@ -425,6 +409,14 @@ func (d *DeploymentDeployer) Sync(ctx context.Context, dl *workloads.Deployment) } vms = append(vms, vm) + case zos.ZMachineLightType: + vmLight, err := workloads.NewVMLightFromWorkload(&w, &deployment, dl.NodeID) + if err != nil { + log.Error().Err(err).Msgf("error parsing vm-light") + continue + } + vmsLight = append(vmsLight, vmLight) + case zos.ZDBType: zdb, err := workloads.NewZDBFromWorkload(&w) if err != nil { @@ -461,12 +453,13 @@ func (d *DeploymentDeployer) Sync(ctx context.Context, dl *workloads.Deployment) } } - dl.Match(disks, qsfs, zdbs, vms, volumes) + dl.Match(disks, qsfs, zdbs, vms, vmsLight, volumes) dl.Disks = disks dl.QSFS = qsfs dl.Zdbs = zdbs dl.Vms = vms + dl.VmsLight = vmsLight dl.Volumes = volumes return nil diff --git a/grid-client/deployer/deployment_deployer_test.go b/grid-client/deployer/deployment_deployer_test.go index ab9694501..458bcca36 100644 --- a/grid-client/deployer/deployment_deployer_test.go +++ b/grid-client/deployer/deployment_deployer_test.go @@ -18,6 +18,7 @@ import ( client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/state" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "github.com/threefoldtech/zos/pkg/gridtypes" "github.com/threefoldtech/zos/pkg/gridtypes/zos" ) @@ -493,8 +494,8 @@ func TestDeploymentDeployerSync(t *testing.T) { dl.NodeDeploymentID = make(map[uint32]uint64) net := constructTestNetwork() - workload := net.ZosWorkload(net.NodesIPRange[nodeID], "", uint16(0), []zos.Peer{}, "", nil) - networkDl := workloads.NewGridDeployment(twinID, []gridtypes.Workload{workload}) + workload := net.ZosWorkload(net.NodesIPRange[nodeID], "", uint16(0), []zosTypes.Peer{}, "", nil) + networkDl := workloads.NewGridDeployment(twinID, 0, []zosTypes.Workload{workload}) d.tfPluginClient.State.CurrentNodeDeployments[nodeID] = append(d.tfPluginClient.State.CurrentNodeDeployments[nodeID], contractID) d.tfPluginClient.State.Networks = state.NetworkState{ @@ -510,7 +511,7 @@ func TestDeploymentDeployerSync(t *testing.T) { cl.EXPECT(). Call(gomock.Any(), twinID, "zos.deployment.get", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - var res *gridtypes.Deployment = result.(*gridtypes.Deployment) + var res *zosTypes.Deployment = result.(*zosTypes.Deployment) *res = networkDl return nil }).AnyTimes() @@ -534,105 +535,91 @@ func TestDeploymentDeployerSync(t *testing.T) { err = json.NewEncoder(log.Writer()).Encode(gridDl.Workloads) assert.NoError(t, err) - for _, zlog := range gridDl.ByType(zos.ZLogsType) { - *zlog.Workload = zlog.WithResults(gridtypes.Result{ - State: gridtypes.StateOk, - }) + for i, w := range gridDl.Workloads { + if w.Type == zos.ZLogsType.String() || w.Type == zos.ZMountType.String() { + gridDl.Workloads[i] = w.WithResults(zosTypes.Result{ + State: zosTypes.StateOk, + }) + } + + if w.Type == zos.ZMachineType.String() { + if w.Name == dl.Vms[0].Name { + gridDl.Workloads[i] = w.WithResults(zosTypes.Result{ + State: zosTypes.StateOk, + Data: mustMarshal(t, zos.ZMachineResult{ + IP: dl.Vms[0].IP, + PlanetaryIP: dl.Vms[0].PlanetaryIP, + }), + }) + } + + if w.Name == dl.Vms[1].Name { + gridDl.Workloads[i] = w.WithResults(zosTypes.Result{ + State: zosTypes.StateOk, + Data: mustMarshal(t, zos.ZMachineResult{ + IP: dl.Vms[1].IP, + PlanetaryIP: dl.Vms[1].PlanetaryIP, + }), + }) + } + } + + if w.Type == zos.QuantumSafeFSType.String() { + gridDl.Workloads[i] = w.WithResults(zosTypes.Result{ + State: zosTypes.StateOk, + Data: mustMarshal(t, zos.QuatumSafeFSResult{ + MetricsEndpoint: dl.QSFS[0].MetricsEndpoint, + }), + }) + } + + if w.Type == zos.ZDBType.String() { + if w.Name == dl.Zdbs[0].Name { + gridDl.Workloads[i] = w.WithResults(zosTypes.Result{ + State: zosTypes.StateOk, + Data: mustMarshal(t, zos.ZDBResult{ + Namespace: dl.Zdbs[0].Namespace, + IPs: dl.Zdbs[0].IPs, + Port: uint(dl.Zdbs[0].Port), + }), + }) + } + + if w.Name == dl.Zdbs[1].Name { + gridDl.Workloads[i] = w.WithResults(zosTypes.Result{ + State: zosTypes.StateOk, + Data: mustMarshal(t, zos.ZDBResult{ + Namespace: dl.Zdbs[1].Namespace, + IPs: dl.Zdbs[1].IPs, + Port: uint(dl.Zdbs[1].Port), + }), + }) + } + } + + if w.Type == zos.PublicIPType.String() { + if w.Name == dl.Vms[0].Name+"ip" { + gridDl.Workloads[i] = w.WithResults(zosTypes.Result{ + State: zosTypes.StateOk, + Data: mustMarshal(t, zos.PublicIPResult{ + IP: gridtypes.MustParseIPNet(dl.Vms[0].ComputedIP), + IPv6: gridtypes.MustParseIPNet(dl.Vms[0].ComputedIP6), + }), + }) + } + + if w.Name == dl.Vms[1].Name+"ip" { + gridDl.Workloads[i] = w.WithResults(zosTypes.Result{ + State: zosTypes.StateOk, + Data: mustMarshal(t, zos.PublicIPResult{ + IP: gridtypes.MustParseIPNet(dl.Vms[1].ComputedIP), + IPv6: gridtypes.MustParseIPNet(dl.Vms[1].ComputedIP6), + }), + }) + } + } } - for _, disk := range gridDl.ByType(zos.ZMountType) { - *disk.Workload = disk.WithResults(gridtypes.Result{ - State: gridtypes.StateOk, - }) - } - - wl, err := gridDl.Get(gridtypes.Name(dl.Vms[0].Name)) - assert.NoError(t, err) - - *wl.Workload = wl.WithResults(gridtypes.Result{ - State: gridtypes.StateOk, - Data: mustMarshal(t, zos.ZMachineResult{ - IP: dl.Vms[0].IP, - PlanetaryIP: dl.Vms[0].PlanetaryIP, - }), - }) - - dataI, err := wl.WorkloadData() - assert.NoError(t, err) - - data, ok := dataI.(*zos.ZMachine) - assert.True(t, ok) - pubIP, err := gridDl.Get(data.Network.PublicIP) - assert.NoError(t, err) - - *pubIP.Workload = pubIP.WithResults(gridtypes.Result{ - State: gridtypes.StateOk, - Data: mustMarshal(t, zos.PublicIPResult{ - IP: gridtypes.MustParseIPNet(dl.Vms[0].ComputedIP), - IPv6: gridtypes.MustParseIPNet(dl.Vms[0].ComputedIP6), - }), - }) - - wl, err = gridDl.Get(gridtypes.Name(dl.Vms[1].Name)) - assert.NoError(t, err) - - dataI, err = wl.WorkloadData() - assert.NoError(t, err) - - data, ok = dataI.(*zos.ZMachine) - assert.True(t, ok) - pubIP, err = gridDl.Get(data.Network.PublicIP) - assert.NoError(t, err) - - *pubIP.Workload = pubIP.WithResults(gridtypes.Result{ - State: gridtypes.StateOk, - Data: mustMarshal(t, zos.PublicIPResult{ - IPv6: gridtypes.MustParseIPNet(dl.Vms[1].ComputedIP6), - }), - }) - - *wl.Workload = wl.WithResults(gridtypes.Result{ - State: gridtypes.StateOk, - Data: mustMarshal(t, zos.ZMachineResult{ - IP: dl.Vms[1].IP, - PlanetaryIP: dl.Vms[1].PlanetaryIP, - }), - }) - - wl, err = gridDl.Get(gridtypes.Name(dl.QSFS[0].Name)) - assert.NoError(t, err) - - *wl.Workload = wl.WithResults(gridtypes.Result{ - State: gridtypes.StateOk, - Data: mustMarshal(t, zos.QuatumSafeFSResult{ - MetricsEndpoint: dl.QSFS[0].MetricsEndpoint, - }), - }) - - wl, err = gridDl.Get(gridtypes.Name(dl.Zdbs[0].Name)) - assert.NoError(t, err) - - *wl.Workload = wl.WithResults(gridtypes.Result{ - State: gridtypes.StateOk, - Data: mustMarshal(t, zos.ZDBResult{ - Namespace: dl.Zdbs[0].Namespace, - IPs: dl.Zdbs[0].IPs, - Port: uint(dl.Zdbs[0].Port), - }), - }) - - wl, err = gridDl.Get(gridtypes.Name(dl.Zdbs[1].Name)) - assert.NoError(t, err) - - *wl.Workload = wl.WithResults(gridtypes.Result{ - State: gridtypes.StateOk, - Data: mustMarshal(t, zos.ZDBResult{ - Namespace: dl.Zdbs[1].Namespace, - IPs: dl.Zdbs[1].IPs, - Port: uint(dl.Zdbs[1].Port), - }), - }) - for i := 0; 2*i < len(gridDl.Workloads); i++ { gridDl.Workloads[i], gridDl.Workloads[len(gridDl.Workloads)-1-i] = gridDl.Workloads[len(gridDl.Workloads)-1-i], gridDl.Workloads[i] } @@ -647,7 +634,7 @@ func TestDeploymentDeployerSync(t *testing.T) { deployer.EXPECT(). GetDeployments(gomock.Any(), map[uint32]uint64{}). - Return(map[uint32]gridtypes.Deployment{nodeID: gridDl}, nil) + Return(map[uint32]zosTypes.Deployment{nodeID: gridDl}, nil) // manager.EXPECT().Commit(context.Background()).AnyTimes() assert.NoError(t, d.Sync(context.Background(), &dl)) assert.Equal(t, dl.Vms, cp.Vms) @@ -662,7 +649,7 @@ func TestDeploymentDeployerSync(t *testing.T) { dl.ContractID = contractID sub.EXPECT().IsValidContract(dl.ContractID).Return(false, nil).AnyTimes() - err := d.syncContract(context.Background(), &dl) + err := d.syncContract(&dl) assert.NoError(t, err) assert.Equal(t, dl.ContractID, uint64(0)) dl.ContractID = contractID @@ -670,7 +657,7 @@ func TestDeploymentDeployerSync(t *testing.T) { deployer.EXPECT(). GetDeployments(gomock.Any(), map[uint32]uint64{nodeID: contractID}). - Return(map[uint32]gridtypes.Deployment{}, nil) + Return(map[uint32]zosTypes.Deployment{}, nil) assert.NoError(t, d.Sync(context.Background(), &dl)) @@ -697,10 +684,10 @@ func ExampleDeploymentDeployer_Deploy() { Name: "network", Description: "network for testing", Nodes: []uint32{nodeID}, - IPRange: gridtypes.NewIPNet(net.IPNet{ + IPRange: zosTypes.IPNet{IPNet: net.IPNet{ IP: net.IPv4(10, 1, 0, 0), Mask: net.CIDRMask(16, 32), - }), + }}, AddWGAccess: false, } @@ -722,7 +709,7 @@ func ExampleDeploymentDeployer_Deploy() { return } - dl := workloads.NewDeployment("vmdeployment", nodeID, "", nil, n.Name, nil, nil, []workloads.VM{vm}, nil, nil) + dl := workloads.NewDeployment("vmdeployment", nodeID, "", nil, n.Name, nil, nil, []workloads.VM{vm}, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &dl) if err != nil { fmt.Println(err) @@ -747,10 +734,10 @@ func ExampleDeploymentDeployer_BatchDeploy() { Name: "network", Description: "network for testing", Nodes: []uint32{nodeID}, - IPRange: gridtypes.NewIPNet(net.IPNet{ + IPRange: zosTypes.IPNet{IPNet: net.IPNet{ IP: net.IPv4(10, 1, 0, 0), Mask: net.CIDRMask(16, 32), - }), + }}, AddWGAccess: false, } @@ -783,8 +770,8 @@ func ExampleDeploymentDeployer_BatchDeploy() { return } - d1 := workloads.NewDeployment("vm1deployment", nodeID, "", nil, n.Name, nil, nil, []workloads.VM{vm1}, nil, nil) - d2 := workloads.NewDeployment("vm2deployment", nodeID, "", nil, n.Name, nil, nil, []workloads.VM{vm2}, nil, nil) + d1 := workloads.NewDeployment("vm1deployment", nodeID, "", nil, n.Name, nil, nil, []workloads.VM{vm1}, nil, nil, nil) + d2 := workloads.NewDeployment("vm2deployment", nodeID, "", nil, n.Name, nil, nil, []workloads.VM{vm2}, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.BatchDeploy(context.Background(), []*workloads.Deployment{&d1, &d2}) if err != nil { fmt.Println(err) diff --git a/grid-client/deployer/deployment_utils.go b/grid-client/deployer/deployment_utils.go index 581e0f8cd..5c1c0c922 100644 --- a/grid-client/deployer/deployment_utils.go +++ b/grid-client/deployer/deployment_utils.go @@ -4,16 +4,16 @@ import ( "crypto/md5" "github.com/pkg/errors" - "github.com/threefoldtech/zos/pkg/gridtypes" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "github.com/threefoldtech/zos/pkg/gridtypes/zos" ) // CountDeploymentPublicIPs counts the public IPs of a deployment -func CountDeploymentPublicIPs(dl gridtypes.Deployment) (uint32, error) { +func CountDeploymentPublicIPs(dl zosTypes.Deployment) (uint32, error) { var res uint32 for _, wl := range dl.Workloads { - if wl.Type == zos.PublicIPType { - data, err := wl.WorkloadData() + if wl.Type == zosTypes.PublicIPType { + data, err := wl.Workload3().WorkloadData() if err != nil { return res, errors.Wrapf(err, "could not parse workload data for workload %s", wl.Name) } @@ -26,7 +26,7 @@ func CountDeploymentPublicIPs(dl gridtypes.Deployment) (uint32, error) { } // HashDeployment returns deployment hash -func HashDeployment(dl gridtypes.Deployment) (string, error) { +func HashDeployment(dl zosTypes.Deployment) (string, error) { md5Hash := md5.New() if err := dl.Challenge(md5Hash); err != nil { return "", err @@ -36,11 +36,11 @@ func HashDeployment(dl gridtypes.Deployment) (string, error) { } // GetWorkloadHashes returns a mapping between workload name to the workload hash -func GetWorkloadHashes(dl gridtypes.Deployment) (map[string]string, error) { +func GetWorkloadHashes(dl zosTypes.Deployment) (map[string]string, error) { hashes := make(map[string]string) for _, w := range dl.Workloads { - key := string(w.Name) + key := w.Name md5Hash := md5.New() if err := w.Challenge(md5Hash); err != nil { return nil, errors.Wrapf(err, "could not get a hash for a workload %s", key) @@ -53,18 +53,18 @@ func GetWorkloadHashes(dl gridtypes.Deployment) (map[string]string, error) { } // SameWorkloadsNames compares names of 2 deployments' workloads -func SameWorkloadsNames(d1 gridtypes.Deployment, d2 gridtypes.Deployment) bool { +func SameWorkloadsNames(d1 zosTypes.Deployment, d2 zosTypes.Deployment) bool { if len(d1.Workloads) != len(d2.Workloads) { return false } names := make(map[string]bool) for _, w := range d1.Workloads { - names[string(w.Name)] = true + names[w.Name] = true } for _, w := range d2.Workloads { - if _, ok := names[string(w.Name)]; !ok { + if _, ok := names[w.Name]; !ok { return false } } @@ -72,11 +72,11 @@ func SameWorkloadsNames(d1 gridtypes.Deployment, d2 gridtypes.Deployment) bool { } // ConstructWorkloadVersions returns a mapping between workload name to the workload version -func ConstructWorkloadVersions(dl gridtypes.Deployment) map[string]uint32 { +func ConstructWorkloadVersions(dl zosTypes.Deployment) map[string]uint32 { versions := make(map[string]uint32) for _, w := range dl.Workloads { - key := string(w.Name) + key := w.Name versions[key] = w.Version } @@ -84,7 +84,7 @@ func ConstructWorkloadVersions(dl gridtypes.Deployment) map[string]uint32 { } // HasWorkload checks if a deployment contains a given workload -func HasWorkload(dl *gridtypes.Deployment, wlType gridtypes.WorkloadType) bool { +func HasWorkload(dl *zosTypes.Deployment, wlType string) bool { for _, wl := range dl.Workloads { if wl.Type == wlType { return true @@ -94,8 +94,8 @@ func HasWorkload(dl *gridtypes.Deployment, wlType gridtypes.WorkloadType) bool { } // Capacity returns the capacity of a deployment -func Capacity(dl gridtypes.Deployment) (gridtypes.Capacity, error) { - cap := gridtypes.Capacity{} +func Capacity(dl zosTypes.Deployment) (zosTypes.Capacity, error) { + cap := zosTypes.Capacity{} for _, wl := range dl.Workloads { wlCap, err := wl.Capacity() if err != nil { diff --git a/grid-client/deployer/deployment_utils_test.go b/grid-client/deployer/deployment_utils_test.go index 7ff0d8c53..507f4dfc0 100644 --- a/grid-client/deployer/deployment_utils_test.go +++ b/grid-client/deployer/deployment_utils_test.go @@ -5,8 +5,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" - "github.com/threefoldtech/zos/pkg/gridtypes" - "github.com/threefoldtech/zos/pkg/gridtypes/zos" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) func TestDeploymentUtils(t *testing.T) { @@ -16,7 +15,7 @@ func TestDeploymentUtils(t *testing.T) { identity := tfPluginClient.Identity twinID := tfPluginClient.TwinID - dl := workloads.NewGridDeployment(twinID, []gridtypes.Workload{}) + dl := workloads.NewGridDeployment(twinID, 0, []zosTypes.Workload{}) dlName, err := deploymentWithNameGateway(identity, twinID, true, 0, backendURLWithTLSPassthrough) assert.NoError(t, err) @@ -54,10 +53,10 @@ func TestDeploymentUtils(t *testing.T) { }) t.Run("deployments workloads exist", func(t *testing.T) { - exists := HasWorkload(&dlName, zos.GatewayFQDNProxyType) + exists := HasWorkload(&dlName, zosTypes.GatewayFQDNProxyType) assert.Equal(t, exists, false) - exists = HasWorkload(&dlName, zos.GatewayNameProxyType) + exists = HasWorkload(&dlName, zosTypes.GatewayNameProxyType) assert.Equal(t, exists, true) }) @@ -65,8 +64,8 @@ func TestDeploymentUtils(t *testing.T) { cap, err := Capacity(dlName) assert.NoError(t, err) assert.Equal(t, cap.CRU, uint64(0)) - assert.Equal(t, cap.SRU, gridtypes.Unit(0)) - assert.Equal(t, cap.MRU, gridtypes.Unit(0)) - assert.Equal(t, cap.HRU, gridtypes.Unit(0)) + assert.Equal(t, cap.SRU, uint64(0)) + assert.Equal(t, cap.MRU, uint64(0)) + assert.Equal(t, cap.HRU, uint64(0)) }) } diff --git a/grid-client/deployer/gateway_fqdn_deployer.go b/grid-client/deployer/gateway_fqdn_deployer.go index d5abbb4a3..f879171c6 100644 --- a/grid-client/deployer/gateway_fqdn_deployer.go +++ b/grid-client/deployer/gateway_fqdn_deployer.go @@ -6,7 +6,7 @@ import ( "github.com/pkg/errors" client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" - "github.com/threefoldtech/zos/pkg/gridtypes" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) // GatewayFQDNDeployer for deploying a GatewayFqdn @@ -55,12 +55,12 @@ func (d *GatewayFQDNDeployer) Validate(ctx context.Context, gw *workloads.Gatewa } // GenerateVersionlessDeployments generates deployments for gatewayFqdn deployer without versions -func (d *GatewayFQDNDeployer) GenerateVersionlessDeployments(ctx context.Context, gw *workloads.GatewayFQDNProxy) (map[uint32]gridtypes.Deployment, error) { - deployments := make(map[uint32]gridtypes.Deployment) +func (d *GatewayFQDNDeployer) GenerateVersionlessDeployments(ctx context.Context, gw *workloads.GatewayFQDNProxy) (map[uint32]zosTypes.Deployment, error) { + deployments := make(map[uint32]zosTypes.Deployment) var err error - dl := workloads.NewGridDeployment(d.tfPluginClient.TwinID, []gridtypes.Workload{}) - dl.Workloads = append(dl.Workloads, gw.ZosWorkload()) + dl := workloads.NewGridDeployment(d.tfPluginClient.TwinID, 0, []zosTypes.Workload{}) + dl.Workloads = append(dl.Workloads, zosTypes.NewWorkloadFromZosWorkload(gw.ZosWorkload())) dl.Metadata, err = gw.GenerateMetadata() if err != nil { @@ -102,7 +102,7 @@ func (d *GatewayFQDNDeployer) Deploy(ctx context.Context, gw *workloads.GatewayF // BatchDeploy deploys multiple deployments using the deployer func (d *GatewayFQDNDeployer) BatchDeploy(ctx context.Context, gws []*workloads.GatewayFQDNProxy) error { - newDeployments := make(map[uint32][]gridtypes.Deployment) + newDeployments := make(map[uint32][]zosTypes.Deployment) newDeploymentsSolutionProvider := make(map[uint32][]*uint64) for _, gw := range gws { @@ -120,7 +120,7 @@ func (d *GatewayFQDNDeployer) BatchDeploy(ctx context.Context, gws []*workloads. newDeploymentsSolutionProvider[nodeID] = nil if _, ok := newDeployments[nodeID]; !ok { - newDeployments[nodeID] = []gridtypes.Deployment{dl} + newDeployments[nodeID] = []zosTypes.Deployment{dl} continue } newDeployments[nodeID] = append(newDeployments[nodeID], dl) @@ -132,7 +132,7 @@ func (d *GatewayFQDNDeployer) BatchDeploy(ctx context.Context, gws []*workloads. // update state // error is not returned immediately before updating state because of untracked failed deployments for _, gw := range gws { - if err := d.updateStateFromDeployments(ctx, gw, newDls); err != nil { + if err := d.updateStateFromDeployments(gw, newDls); err != nil { return errors.Wrapf(err, "failed to update gateway fqdn '%s' state", gw.Name) } } @@ -160,7 +160,7 @@ func (d *GatewayFQDNDeployer) Cancel(ctx context.Context, gw *workloads.GatewayF return nil } -func (d *GatewayFQDNDeployer) updateStateFromDeployments(ctx context.Context, gw *workloads.GatewayFQDNProxy, newDls map[uint32][]gridtypes.Deployment) error { +func (d *GatewayFQDNDeployer) updateStateFromDeployments(gw *workloads.GatewayFQDNProxy, newDls map[uint32][]zosTypes.Deployment) error { gw.NodeDeploymentID = map[uint32]uint64{} for _, newDl := range newDls[gw.NodeID] { @@ -207,7 +207,7 @@ func (d *GatewayFQDNDeployer) Sync(ctx context.Context, gw *workloads.GatewayFQD } dl := dls[gw.NodeID] - wl, _ := dl.Get(gridtypes.Name(gw.Name)) + wl, _ := dl.Get(gw.Name) gwWorkload := workloads.GatewayFQDNProxy{} gw.Backends = gwWorkload.Backends @@ -217,7 +217,7 @@ func (d *GatewayFQDNDeployer) Sync(ctx context.Context, gw *workloads.GatewayFQD gw.Network = gwWorkload.Network if wl != nil && wl.Result.State.IsOkay() { - gwWorkload, err := workloads.NewGatewayFQDNProxyFromZosWorkload(*wl.Workload) + gwWorkload, err := workloads.NewGatewayFQDNProxyFromZosWorkload(*wl.Workload.Workload3()) gw.Backends = gwWorkload.Backends gw.Name = gwWorkload.Name gw.FQDN = gwWorkload.FQDN diff --git a/grid-client/deployer/gateway_fqdn_deployer_test.go b/grid-client/deployer/gateway_fqdn_deployer_test.go index 866f0be77..73f702c1b 100644 --- a/grid-client/deployer/gateway_fqdn_deployer_test.go +++ b/grid-client/deployer/gateway_fqdn_deployer_test.go @@ -17,6 +17,7 @@ import ( client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/state" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" proxyTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-proxy/pkg/types" "github.com/threefoldtech/zos/pkg/gridtypes" "github.com/threefoldtech/zos/pkg/gridtypes/zos" @@ -128,11 +129,11 @@ func TestFQDNDeployer(t *testing.T) { dls, err := d.GenerateVersionlessDeployments(context.Background(), &gw) assert.NoError(t, err) - testDl := workloads.NewGridDeployment(twinID, []gridtypes.Workload{ + testDl := workloads.NewGridDeployment(twinID, 0, []zosTypes.Workload{ { Version: 0, - Type: zos.GatewayFQDNProxyType, - Name: gridtypes.Name(gw.Name), + Type: zosTypes.GatewayFQDNProxyType, + Name: gw.Name, Data: gridtypes.MustMarshal(zos.GatewayFQDNProxy{ GatewayBase: zos.GatewayBase{ TLSPassthrough: gw.TLSPassthrough, @@ -144,7 +145,7 @@ func TestFQDNDeployer(t *testing.T) { }) testDl.Metadata = "{\"version\":3,\"type\":\"Gateway Fqdn\",\"name\":\"name\",\"projectName\":\"name\"}" - assert.Equal(t, dls, map[uint32]gridtypes.Deployment{ + assert.Equal(t, dls, map[uint32]zosTypes.Deployment{ nodeID: testDl, }) }) @@ -319,7 +320,7 @@ func TestFQDNDeployer(t *testing.T) { assert.NoError(t, err) dl := dls[nodeID] - dl.Workloads[0].Result.State = gridtypes.StateOk + dl.Workloads[0].Result.State = zosTypes.StateOk dl.Workloads[0].Result.Data, err = json.Marshal(zos.GatewayFQDNResult{}) assert.NoError(t, err) @@ -329,8 +330,8 @@ func TestFQDNDeployer(t *testing.T) { deployer.EXPECT(). GetDeployments(gomock.Any(), map[uint32]uint64{nodeID: contractID}). - DoAndReturn(func(ctx context.Context, _ map[uint32]uint64) (map[uint32]gridtypes.Deployment, error) { - return map[uint32]gridtypes.Deployment{nodeID: dl}, nil + DoAndReturn(func(ctx context.Context, _ map[uint32]uint64) (map[uint32]zosTypes.Deployment, error) { + return map[uint32]zosTypes.Deployment{nodeID: dl}, nil }) gw.FQDN = "123" @@ -357,8 +358,8 @@ func TestFQDNDeployer(t *testing.T) { deployer.EXPECT(). GetDeployments(gomock.Any(), map[uint32]uint64{nodeID: contractID}). - DoAndReturn(func(ctx context.Context, _ map[uint32]uint64) (map[uint32]gridtypes.Deployment, error) { - return map[uint32]gridtypes.Deployment{nodeID: dl}, nil + DoAndReturn(func(ctx context.Context, _ map[uint32]uint64) (map[uint32]zosTypes.Deployment, error) { + return map[uint32]zosTypes.Deployment{nodeID: dl}, nil }) gw.FQDN = "123" diff --git a/grid-client/deployer/gateway_name_deployer.go b/grid-client/deployer/gateway_name_deployer.go index 8ea1f7ac2..706f28f21 100644 --- a/grid-client/deployer/gateway_name_deployer.go +++ b/grid-client/deployer/gateway_name_deployer.go @@ -7,7 +7,7 @@ import ( "github.com/pkg/errors" client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" - "github.com/threefoldtech/zos/pkg/gridtypes" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) // GatewayNameDeployer for deploying a GatewayName @@ -42,12 +42,12 @@ func (d *GatewayNameDeployer) Validate(ctx context.Context, gw *workloads.Gatewa } // GenerateVersionlessDeployments generates deployments for gateway name deployer without versions -func (d *GatewayNameDeployer) GenerateVersionlessDeployments(ctx context.Context, gw *workloads.GatewayNameProxy) (map[uint32]gridtypes.Deployment, error) { - deployments := make(map[uint32]gridtypes.Deployment) +func (d *GatewayNameDeployer) GenerateVersionlessDeployments(ctx context.Context, gw *workloads.GatewayNameProxy) (map[uint32]zosTypes.Deployment, error) { + deployments := make(map[uint32]zosTypes.Deployment) var err error - dl := workloads.NewGridDeployment(d.tfPluginClient.TwinID, []gridtypes.Workload{}) - dl.Workloads = append(dl.Workloads, gw.ZosWorkload()) + dl := workloads.NewGridDeployment(d.tfPluginClient.TwinID, 0, []zosTypes.Workload{}) + dl.Workloads = append(dl.Workloads, zosTypes.NewWorkloadFromZosWorkload(gw.ZosWorkload())) dl.Metadata, err = gw.GenerateMetadata() if err != nil { @@ -104,7 +104,7 @@ func (d *GatewayNameDeployer) Deploy(ctx context.Context, gw *workloads.GatewayN // BatchDeploy deploys multiple deployments using the deployer func (d *GatewayNameDeployer) BatchDeploy(ctx context.Context, gws []*workloads.GatewayNameProxy) error { - newDeployments := make(map[uint32][]gridtypes.Deployment) + newDeployments := make(map[uint32][]zosTypes.Deployment) newDeploymentsSolutionProvider := make(map[uint32][]*uint64) for _, gw := range gws { @@ -132,7 +132,7 @@ func (d *GatewayNameDeployer) BatchDeploy(ctx context.Context, gws []*workloads. newDeploymentsSolutionProvider[nodeID] = nil if _, ok := newDeployments[nodeID]; !ok { - newDeployments[nodeID] = []gridtypes.Deployment{dl} + newDeployments[nodeID] = []zosTypes.Deployment{dl} continue } newDeployments[nodeID] = append(newDeployments[nodeID], dl) @@ -144,7 +144,7 @@ func (d *GatewayNameDeployer) BatchDeploy(ctx context.Context, gws []*workloads. // update state // error is not returned immediately before updating state because of untracked failed deployments for _, gw := range gws { - if err := d.updateStateFromDeployments(ctx, gw, newDls); err != nil { + if err := d.updateStateFromDeployments(gw, newDls); err != nil { return errors.Wrapf(err, "failed to update gateway fqdn '%s' state", gw.Name) } } @@ -174,7 +174,7 @@ func (d *GatewayNameDeployer) Cancel(ctx context.Context, gw *workloads.GatewayN return nil } -func (d *GatewayNameDeployer) updateStateFromDeployments(ctx context.Context, gw *workloads.GatewayNameProxy, newDls map[uint32][]gridtypes.Deployment) error { +func (d *GatewayNameDeployer) updateStateFromDeployments(gw *workloads.GatewayNameProxy, newDls map[uint32][]zosTypes.Deployment) error { gw.NodeDeploymentID = map[uint32]uint64{} for _, newDl := range newDls[gw.NodeID] { @@ -241,7 +241,7 @@ func (d *GatewayNameDeployer) Sync(ctx context.Context, gw *workloads.GatewayNam return errors.Wrap(err, "could not get deployment objects") } dl := dls[gw.NodeID] - wl, _ := dl.Get(gridtypes.Name(gw.Name)) + wl, _ := dl.Get(gw.Name) gwWorkload := workloads.GatewayNameProxy{} gw.Backends = gwWorkload.Backends @@ -252,7 +252,7 @@ func (d *GatewayNameDeployer) Sync(ctx context.Context, gw *workloads.GatewayNam // if the node acknowledges it, we are golden if wl != nil && wl.Result.State.IsOkay() { - gwWorkload, err := workloads.NewGatewayNameProxyFromZosWorkload(*wl.Workload) + gwWorkload, err := workloads.NewGatewayNameProxyFromZosWorkload(*wl.Workload.Workload3()) gw.Backends = gwWorkload.Backends gw.Name = gwWorkload.Name gw.FQDN = gwWorkload.FQDN diff --git a/grid-client/deployer/gateway_name_deployer_test.go b/grid-client/deployer/gateway_name_deployer_test.go index 5a5149437..ad6823cd1 100644 --- a/grid-client/deployer/gateway_name_deployer_test.go +++ b/grid-client/deployer/gateway_name_deployer_test.go @@ -16,6 +16,7 @@ import ( client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/state" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "github.com/threefoldtech/zos/pkg/gridtypes" "github.com/threefoldtech/zos/pkg/gridtypes/zos" ) @@ -108,11 +109,11 @@ func TestNameDeployer(t *testing.T) { dls, err := d.GenerateVersionlessDeployments(context.Background(), &gw) assert.NoError(t, err) - testDl := workloads.NewGridDeployment(twinID, []gridtypes.Workload{ + testDl := workloads.NewGridDeployment(twinID, 0, []zosTypes.Workload{ { Version: 0, - Type: zos.GatewayNameProxyType, - Name: gridtypes.Name(gw.Name), + Type: zosTypes.GatewayNameProxyType, + Name: gw.Name, Data: gridtypes.MustMarshal(zos.GatewayNameProxy{ GatewayBase: zos.GatewayBase{ TLSPassthrough: gw.TLSPassthrough, @@ -124,7 +125,7 @@ func TestNameDeployer(t *testing.T) { }) testDl.Metadata = "{\"version\":3,\"type\":\"Gateway Name\",\"name\":\"name\",\"projectName\":\"name\"}" - assert.Equal(t, dls, map[uint32]gridtypes.Deployment{ + assert.Equal(t, dls, map[uint32]zosTypes.Deployment{ nodeID: testDl, }) }) @@ -345,7 +346,7 @@ func TestNameDeployer(t *testing.T) { dl := dls[nodeID] - dl.Workloads[0].Result.State = gridtypes.StateOk + dl.Workloads[0].Result.State = zosTypes.StateOk dl.Workloads[0].Result.Data, err = json.Marshal(zos.GatewayProxyResult{FQDN: "name.com"}) assert.NoError(t, err) @@ -359,7 +360,7 @@ func TestNameDeployer(t *testing.T) { deployer.EXPECT(). GetDeployments(gomock.Any(), map[uint32]uint64{nodeID: contractID}). - Return(map[uint32]gridtypes.Deployment{nodeID: dl}, nil) + Return(map[uint32]zosTypes.Deployment{nodeID: dl}, nil) gw.FQDN = "123" err = d.Sync(context.Background(), &gw) @@ -392,7 +393,7 @@ func TestNameDeployer(t *testing.T) { deployer.EXPECT(). GetDeployments(gomock.Any(), map[uint32]uint64{nodeID: contractID}). - Return(map[uint32]gridtypes.Deployment{nodeID: dl}, nil) + Return(map[uint32]zosTypes.Deployment{nodeID: dl}, nil) gw.FQDN = "123" err = d.Sync(context.Background(), &gw) diff --git a/grid-client/deployer/k8s_deployer.go b/grid-client/deployer/k8s_deployer.go index 8fda15711..f907196f2 100644 --- a/grid-client/deployer/k8s_deployer.go +++ b/grid-client/deployer/k8s_deployer.go @@ -11,6 +11,7 @@ import ( zerolog "github.com/rs/zerolog/log" client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "github.com/threefoldtech/zos/pkg/gridtypes" "github.com/threefoldtech/zos/pkg/gridtypes/zos" ) @@ -60,23 +61,27 @@ func (d *K8sDeployer) Validate(ctx context.Context, k8sCluster *workloads.K8sClu } // GenerateVersionlessDeployments generates a new deployment without a version -func (d *K8sDeployer) GenerateVersionlessDeployments(ctx context.Context, k8sCluster *workloads.K8sCluster) (map[uint32]gridtypes.Deployment, error) { +func (d *K8sDeployer) GenerateVersionlessDeployments(ctx context.Context, k8sCluster *workloads.K8sCluster) (map[uint32]zosTypes.Deployment, error) { err := d.assignNodesIPs(k8sCluster) if err != nil { return nil, errors.Wrap(err, "failed to assign node ips") } - deployments := make(map[uint32]gridtypes.Deployment) - nodeWorkloads := make(map[uint32][]gridtypes.Workload) + deployments := make(map[uint32]zosTypes.Deployment) + nodeWorkloads := make(map[uint32][]zosTypes.Workload) masterWorkloads := k8sCluster.Master.MasterZosWorkload(k8sCluster) - nodeWorkloads[k8sCluster.Master.NodeID] = append(nodeWorkloads[k8sCluster.Master.NodeID], masterWorkloads...) + for _, m := range masterWorkloads { + nodeWorkloads[k8sCluster.Master.NodeID] = append(nodeWorkloads[k8sCluster.Master.NodeID], zosTypes.NewWorkloadFromZosWorkload(m)) + } for _, w := range k8sCluster.Workers { workerWorkloads := w.WorkerZosWorkload(k8sCluster) - nodeWorkloads[w.NodeID] = append(nodeWorkloads[w.NodeID], workerWorkloads...) + for _, w := range workerWorkloads { + nodeWorkloads[k8sCluster.Master.NodeID] = append(nodeWorkloads[k8sCluster.Master.NodeID], zosTypes.NewWorkloadFromZosWorkload(w)) + } } for node, ws := range nodeWorkloads { - dl := workloads.NewGridDeployment(d.tfPluginClient.TwinID, ws) + dl := workloads.NewGridDeployment(d.tfPluginClient.TwinID, 0, ws) dl.Metadata, err = k8sCluster.GenerateMetadata() if err != nil { return nil, errors.Wrapf(err, "failed to generate deployment %s metadata", k8sCluster.Master.Name) @@ -128,7 +133,7 @@ func (d *K8sDeployer) Deploy(ctx context.Context, k8sCluster *workloads.K8sClust // BatchDeploy deploys multiple clusters using the deployer func (d *K8sDeployer) BatchDeploy(ctx context.Context, k8sClusters []*workloads.K8sCluster) error { - newDeployments := make(map[uint32][]gridtypes.Deployment) + newDeployments := make(map[uint32][]zosTypes.Deployment) newDeploymentsSolutionProvider := make(map[uint32][]*uint64) for _, k8sCluster := range k8sClusters { @@ -157,7 +162,7 @@ func (d *K8sDeployer) BatchDeploy(ctx context.Context, k8sClusters []*workloads. newDeploymentsSolutionProvider[nodeID] = nil if _, ok := newDeployments[nodeID]; !ok { - newDeployments[nodeID] = []gridtypes.Deployment{dl} + newDeployments[nodeID] = []zosTypes.Deployment{dl} continue } newDeployments[nodeID] = append(newDeployments[nodeID], dl) @@ -169,7 +174,7 @@ func (d *K8sDeployer) BatchDeploy(ctx context.Context, k8sClusters []*workloads. // update deployments state // error is not returned immediately before updating state because of untracked failed deployments for _, k8sCluster := range k8sClusters { - if err := d.updateStateFromDeployments(ctx, k8sCluster, newDls); err != nil { + if err := d.updateStateFromDeployments(k8sCluster, newDls); err != nil { return errors.Wrapf(err, "failed to update cluster with master name '%s' state", k8sCluster.Master.Name) } } @@ -205,7 +210,7 @@ func (d *K8sDeployer) Cancel(ctx context.Context, k8sCluster *workloads.K8sClust return nil } -func (d *K8sDeployer) updateStateFromDeployments(ctx context.Context, k8sCluster *workloads.K8sCluster, newDl map[uint32][]gridtypes.Deployment) error { +func (d *K8sDeployer) updateStateFromDeployments(k8sCluster *workloads.K8sCluster, newDl map[uint32][]zosTypes.Deployment) error { k8sNodes := []uint32{k8sCluster.Master.NodeID} for _, w := range k8sCluster.Workers { k8sNodes = append(k8sNodes, w.NodeID) @@ -242,7 +247,7 @@ func (d *K8sDeployer) updateStateFromDeployments(ctx context.Context, k8sCluster // UpdateFromRemote update a k8s cluster func (d *K8sDeployer) UpdateFromRemote(ctx context.Context, k8sCluster *workloads.K8sCluster) error { - if err := d.removeDeletedContracts(ctx, k8sCluster); err != nil { + if err := d.removeDeletedContracts(k8sCluster); err != nil { return errors.Wrap(err, "failed to remove deleted contracts") } currentDeployments, err := d.deployer.GetDeployments(ctx, k8sCluster.NodeDeploymentID) @@ -255,8 +260,8 @@ func (d *K8sDeployer) UpdateFromRemote(ctx context.Context, k8sCluster *workload // calculate k's properties from the currently deployed deployments for _, dl := range currentDeployments { for _, w := range dl.Workloads { - if w.Type == zos.ZMachineType { - d, err := w.WorkloadData() + if w.Type == zosTypes.ZMachineType { + d, err := w.Workload3().WorkloadData() if err != nil { zerolog.Error().Err(err).Msg("failed to get workload data") } @@ -296,34 +301,34 @@ func (d *K8sDeployer) UpdateFromRemote(ctx context.Context, k8sCluster *workload diskSize := make(map[string]uint64) for node, dl := range currentDeployments { for _, w := range dl.Workloads { - if w.Type == zos.ZMachineType { - workloadNodeID[string(w.Name)] = node - workloadObj[string(w.Name)] = w + if w.Type == zosTypes.ZMachineType { + workloadNodeID[w.Name] = node + workloadObj[w.Name] = *w.Workload3() - } else if w.Type == zos.PublicIPType { + } else if w.Type == zosTypes.PublicIPType { d := zos.PublicIPResult{} if err := json.Unmarshal(w.Result.Data, &d); err != nil { return errors.Wrap(err, "failed to load public ip data") } - publicIPs[string(w.Name)] = d.IP.String() - publicIP6s[string(w.Name)] = d.IPv6.String() - } else if w.Type == zos.ZMountType { - d, err := w.WorkloadData() + publicIPs[w.Name] = d.IP.String() + publicIP6s[w.Name] = d.IPv6.String() + } else if w.Type == zosTypes.ZMountType { + d, err := w.Workload3().WorkloadData() if err != nil { return errors.Wrap(err, "failed to load disk data") } - diskSize[string(w.Name)] = uint64(d.(*zos.ZMount).Size / gridtypes.Gigabyte) + diskSize[w.Name] = uint64(d.(*zos.ZMount).Size) / zosTypes.Gigabyte } } } for _, dl := range currentDeployments { for _, w := range dl.Workloads { - if w.Type == zos.ZMachineType { + if w.Type == zosTypes.ZMachineType { publicIPKey := fmt.Sprintf("%sip", w.Name) diskKey := fmt.Sprintf("%sdisk", w.Name) - workloadDiskSize[string(w.Name)] = diskSize[diskKey] - workloadComputedIP[string(w.Name)] = publicIPs[publicIPKey] - workloadComputedIP6[string(w.Name)] = publicIP6s[publicIPKey] + workloadDiskSize[w.Name] = diskSize[diskKey] + workloadComputedIP[w.Name] = publicIPs[publicIPKey] + workloadComputedIP6[w.Name] = publicIP6s[publicIPKey] } } } @@ -390,7 +395,7 @@ func (d *K8sDeployer) UpdateFromRemote(ctx context.Context, k8sCluster *workload return nil } -func (d *K8sDeployer) removeDeletedContracts(ctx context.Context, k8sCluster *workloads.K8sCluster) error { +func (d *K8sDeployer) removeDeletedContracts(k8sCluster *workloads.K8sCluster) error { sub := d.tfPluginClient.SubstrateConn nodeDeploymentID := make(map[uint32]uint64) for nodeID, deploymentID := range k8sCluster.NodeDeploymentID { diff --git a/grid-client/deployer/k8s_deployer_test.go b/grid-client/deployer/k8s_deployer_test.go index 830f43b2f..0b4bfe124 100644 --- a/grid-client/deployer/k8s_deployer_test.go +++ b/grid-client/deployer/k8s_deployer_test.go @@ -17,6 +17,7 @@ import ( "github.com/threefoldtech/tfgrid-sdk-go/grid-client/state" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/subi" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "github.com/threefoldtech/zos/pkg/gridtypes" ) @@ -173,10 +174,14 @@ func TestK8sDeployer(t *testing.T) { } wl := nodeWorkloads[nodeID] - testDl := workloads.NewGridDeployment(d.tfPluginClient.TwinID, wl) + var zosWls []zosTypes.Workload + for _, w := range wl { + zosWls = append(zosWls, zosTypes.NewWorkloadFromZosWorkload(w)) + } + testDl := workloads.NewGridDeployment(d.tfPluginClient.TwinID, 0, zosWls) testDl.Metadata = "{\"version\":3,\"type\":\"kubernetes\",\"name\":\"K8sForTesting\",\"projectName\":\"kubernetes/K8sForTesting\"}" - assert.Equal(t, dls, map[uint32]gridtypes.Deployment{ + assert.Equal(t, dls, map[uint32]zosTypes.Deployment{ nodeID: testDl, }) }) @@ -335,10 +340,10 @@ func ExampleK8sDeployer_Deploy() { Name: "network", Description: "network for testing", Nodes: []uint32{nodeID}, - IPRange: gridtypes.NewIPNet(net.IPNet{ + IPRange: zosTypes.IPNet{IPNet: net.IPNet{ IP: net.IPv4(10, 1, 0, 0), Mask: net.CIDRMask(16, 32), - }), + }}, AddWGAccess: false, } @@ -409,10 +414,10 @@ func ExampleK8sDeployer_BatchDeploy() { Name: "network", Description: "network for testing", Nodes: []uint32{nodeID}, - IPRange: gridtypes.NewIPNet(net.IPNet{ + IPRange: zosTypes.IPNet{IPNet: net.IPNet{ IP: net.IPv4(10, 1, 0, 0), Mask: net.CIDRMask(16, 32), - }), + }}, AddWGAccess: false, } diff --git a/grid-client/deployer/network_deployer.go b/grid-client/deployer/network_deployer.go index cd79f8091..cc8ce4e08 100644 --- a/grid-client/deployer/network_deployer.go +++ b/grid-client/deployer/network_deployer.go @@ -2,21 +2,15 @@ package deployer import ( "context" - "encoding/json" "fmt" - "math/rand" "net" "slices" - "sync" "github.com/hashicorp/go-multierror" "github.com/pkg/errors" "github.com/rs/zerolog/log" - substrate "github.com/threefoldtech/tfchain/clients/tfchain-client-go" - client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" - "github.com/threefoldtech/zos/pkg/gridtypes" - "github.com/threefoldtech/zos/pkg/gridtypes/zos" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) @@ -36,117 +30,55 @@ func NewNetworkDeployer(tfPluginClient *TFPluginClient) NetworkDeployer { } // Validate validates a network deployer -func (d *NetworkDeployer) Validate(ctx context.Context, znets []*workloads.ZNet) ([]*workloads.ZNet, error) { +func (d *NetworkDeployer) Validate(ctx context.Context, networks []workloads.Network) ([]workloads.Network, error) { sub := d.tfPluginClient.SubstrateConn var multiErr error if err := validateAccountBalanceForExtrinsics(sub, d.tfPluginClient.Identity); err != nil { return nil, err } - filteredZNets := make([]*workloads.ZNet, 0) - for _, znet := range znets { + + filteredZNets := make([]workloads.Network, 0) + + for _, znet := range networks { if err := znet.Validate(); err != nil { multiErr = multierror.Append(multiErr, err) continue } - if err := d.InvalidateBrokenAttributes(znet); err != nil { + if err := znet.InvalidateBrokenAttributes(d.tfPluginClient.SubstrateConn, d.tfPluginClient.NcPool); err != nil { multiErr = multierror.Append(multiErr, err) continue } + filteredZNets = append(filteredZNets, znet) } + return filteredZNets, multiErr } // GenerateVersionlessDeployments generates deployments for network deployer without versions. -func (d *NetworkDeployer) GenerateVersionlessDeployments(ctx context.Context, znets []*workloads.ZNet) (map[uint32][]gridtypes.Deployment, error) { - deployments := make(map[uint32][]gridtypes.Deployment) +func (d *NetworkDeployer) GenerateVersionlessDeployments(ctx context.Context, zNets []workloads.Network) (map[uint32][]zos.Deployment, error) { + deployments := make(map[uint32][]zos.Deployment) endpoints := make(map[uint32]net.IP) nodeUsedPorts := make(map[uint32][]uint16) - allNodes := make(map[uint32]struct{}) var multiErr error - for _, znet := range znets { - for _, node := range znet.Nodes { - allNodes[node] = struct{}{} - } - if znet.PublicNodeID != 0 { - allNodes[znet.PublicNodeID] = struct{}{} - } - } - - var wg sync.WaitGroup - var mu sync.Mutex - - for nodeID := range allNodes { - wg.Add(1) - go func(nodeID uint32) { - defer wg.Done() - endpoint, usedPorts, err := d.getNodeEndpointAndPorts(ctx, nodeID) - mu.Lock() - defer mu.Unlock() - if err != nil { - multiErr = multierror.Append(multiErr, err) - return - } - endpoints[nodeID] = endpoint - nodeUsedPorts[nodeID] = usedPorts - }(nodeID) - - } - wg.Wait() - - var publicNode uint32 - var err error - for nodeID := range allNodes { - // we check first that there is a public node in all nodes in the networks. - // that way we can save extra requests to get a new public node - // and its used ports and endpoints. this public node also might not be used - // as access node if there is no need for it. - if endpoints[nodeID] != nil && endpoints[nodeID].To4() != nil { - publicNode = nodeID - break - } - } - - // if none of the nodes used are public (have ipv4 endpoint) we do - // an extra check that at least one network needs a public node before - // fetching a public node. we can use this one node as an access point - // to all networks that need it. - if publicNode == 0 && needPublicNode(znets) { - publicNode, err = GetPublicNode(ctx, *d.tfPluginClient, nil) - // we don't return immediately here as there might be some - // networks that don't need a public node so they should continue - // processing fine - if err != nil { - multiErr = multierror.Append( - multiErr, - fmt.Errorf("failed to get public node: %w", err), - ) - } - } - // here we check that the we managed to get a public node - // and that public node wasn't already processed. if we got - // an error while getting public node, then node id will be 0 - // and we will skip getting its data. - if _, ok := endpoints[publicNode]; !ok && publicNode != 0 { - endpoint, usedPorts, err := d.getNodeEndpointAndPorts(ctx, publicNode) - if err != nil { - multiErr = multierror.Append(multiErr, err) - } else { - endpoints[publicNode] = endpoint - nodeUsedPorts[publicNode] = usedPorts - } + allNodes, publicNode, err := d.calcPublicNode(ctx, zNets, endpoints) + if err != nil { + multiErr = multierror.Append(multiErr, err) } - for _, znet := range znets { - dls, err := d.generateDeployments(znet, endpoints, nodeUsedPorts, publicNode) + for _, znet := range zNets { + deployment, err := znet.GenerateVersionlessDeployments( + ctx, d.tfPluginClient.NcPool, d.tfPluginClient.SubstrateConn, + d.tfPluginClient.TwinID, publicNode, allNodes, endpoints, nodeUsedPorts, + ) if err != nil { multiErr = multierror.Append(multiErr, err) continue } - for nodeID, dl := range dls { + for nodeID, dl := range deployment { deployments[nodeID] = append(deployments[nodeID], dl) } } @@ -154,269 +86,19 @@ func (d *NetworkDeployer) GenerateVersionlessDeployments(ctx context.Context, zn return deployments, multiErr } -func (d *NetworkDeployer) getNodeEndpointAndPorts(ctx context.Context, nodeID uint32) (net.IP, []uint16, error) { - nodeClient, err := d.tfPluginClient.NcPool.GetNodeClient(d.tfPluginClient.SubstrateConn, nodeID) - if err != nil { - return nil, nil, fmt.Errorf("could not get node %d client: %w", nodeID, err) - } - endpoint, err := nodeClient.GetNodeEndpoint(ctx) - if err != nil && !errors.Is(err, client.ErrNoAccessibleInterfaceFound) { - return nil, nil, fmt.Errorf("failed to get node %d endpoint: %w", nodeID, err) - } - usedPorts, err := nodeClient.NetworkListWGPorts(ctx) - if err != nil { - return nil, nil, fmt.Errorf("failed to get node %d used ports: %w", nodeID, err) - } - return endpoint, usedPorts, nil -} - -func needPublicNode(znets []*workloads.ZNet) bool { - // entering here means all nodes for all networks are either hidden or ipv6 only - // we need an extra public node in two cases: - // - the user asked for WireGuard access - // - there are multiple nodes in the network and none of them have ipv4. - // because networks must communicate through ipv4 - for _, znet := range znets { - if znet.AddWGAccess { - return true - } - if len(znet.Nodes) > 1 { - return true - } - } - return false -} - -func (d *NetworkDeployer) generateDeployments(znet *workloads.ZNet, endpointIPs map[uint32]net.IP, usedPorts map[uint32][]uint16, publicNode uint32) (map[uint32]gridtypes.Deployment, error) { - deployments := make(map[uint32]gridtypes.Deployment) - - log.Debug().Msgf("nodes: %v", znet.Nodes) - - endpoints := make(map[uint32]string) - hiddenNodes := make([]uint32, 0) - accessibleNodes := make([]uint32, 0) - var ipv4Node uint32 - - for _, nodeID := range znet.Nodes { - if _, ok := endpointIPs[nodeID]; !ok { - // means the network has a failing node - // we will skip generating deployments for it. - return nil, fmt.Errorf("failed to process network %s", znet.Name) - } - if endpointIPs[nodeID] == nil { - hiddenNodes = append(hiddenNodes, nodeID) - } else if endpointIPs[nodeID].To4() != nil { - accessibleNodes = append(accessibleNodes, nodeID) - ipv4Node = nodeID - endpoints[nodeID] = endpointIPs[nodeID].String() - } else { - accessibleNodes = append(accessibleNodes, nodeID) - endpoints[nodeID] = fmt.Sprintf("[%s]", endpointIPs[nodeID].String()) - } - } - - needsIPv4Access := znet.AddWGAccess || (len(hiddenNodes) != 0 && len(hiddenNodes)+len(accessibleNodes) > 1) - if needsIPv4Access { - if znet.PublicNodeID != 0 { // it's set - // if public node id is already set, it should be added to accessible nodes - if !workloads.Contains(accessibleNodes, znet.PublicNodeID) { - accessibleNodes = append(accessibleNodes, znet.PublicNodeID) - } - } else if ipv4Node != 0 { // there's one in the network original nodes - znet.PublicNodeID = ipv4Node - } else if _, ok := endpointIPs[publicNode]; !ok || publicNode == 0 { - // that means either we didn't find a public node - // or we failed to get its endpoint, so can't continue with the network - return nil, fmt.Errorf("failed to get public node for %s", znet.Name) - } else { - znet.PublicNodeID = publicNode - accessibleNodes = append(accessibleNodes, publicNode) - endpoints[publicNode] = endpointIPs[publicNode].String() - } - } - - allNodes := append(hiddenNodes, accessibleNodes...) - if err := znet.AssignNodesIPs(allNodes); err != nil { - return nil, errors.Wrap(err, "could not assign node ips") - } - if err := znet.AssignNodesWGKey(allNodes); err != nil { - return nil, errors.Wrap(err, "could not assign node wg keys") - } - if znet.WGPort == nil { - znet.WGPort = make(map[uint32]int) - } - - // assign WireGuard ports - for _, nodeID := range allNodes { - nodeUsedPorts := usedPorts[nodeID] - p := uint16(rand.Intn(32768-1024) + 1024) - for slices.Contains(nodeUsedPorts, p) { - p = uint16(rand.Intn(32768-1024) + 1024) - } - nodeUsedPorts = append(nodeUsedPorts, p) - usedPorts[nodeID] = nodeUsedPorts - znet.WGPort[nodeID] = int(p) - } - - nonAccessibleIPRanges := []gridtypes.IPNet{} - for _, nodeID := range hiddenNodes { - r := znet.NodesIPRange[nodeID] - nonAccessibleIPRanges = append(nonAccessibleIPRanges, r) - nonAccessibleIPRanges = append(nonAccessibleIPRanges, workloads.WgIP(r)) - } - if znet.AddWGAccess { - r := znet.ExternalIP - nonAccessibleIPRanges = append(nonAccessibleIPRanges, *r) - nonAccessibleIPRanges = append(nonAccessibleIPRanges, workloads.WgIP(*r)) - } - - log.Debug().Msgf("hidden nodes: %v", hiddenNodes) - log.Debug().Uint32("public node", znet.PublicNodeID) - log.Debug().Msgf("accessible nodes: %v", accessibleNodes) - log.Debug().Msgf("non accessible ip ranges: %v", nonAccessibleIPRanges) - - if znet.AddWGAccess { - // if no wg private key, it should be generated - if znet.ExternalSK.String() == workloads.ExternalSKZeroValue { - wgSK, err := wgtypes.GeneratePrivateKey() - if err != nil { - return nil, errors.Wrapf(err, "failed to generate wireguard secret key for network: %s", znet.Name) - } - znet.ExternalSK = wgSK - } - - znet.AccessWGConfig = workloads.GenerateWGConfig( - workloads.WgIP(*znet.ExternalIP).IP.String(), - znet.ExternalSK.String(), - znet.Keys[znet.PublicNodeID].PublicKey().String(), - fmt.Sprintf("%s:%d", endpoints[znet.PublicNodeID], znet.WGPort[znet.PublicNodeID]), - znet.IPRange.String(), - ) - } - - externalIP := "" - if znet.ExternalIP != nil { - externalIP = znet.ExternalIP.String() - } - metadata := workloads.NetworkMetaData{ - Version: workloads.Version, - UserAccesses: []workloads.UserAccess{ - { - Subnet: externalIP, - PrivateKey: znet.ExternalSK.String(), - NodeID: znet.PublicNodeID, - }, - }, - } - - metadataBytes, err := json.Marshal(metadata) - if err != nil { - return nil, errors.Wrapf(err, "failed to marshal network metadata") - } - - // accessible nodes deployments - for _, nodeID := range accessibleNodes { - peers := make([]zos.Peer, 0, len(znet.Nodes)) - for _, peerNodeID := range accessibleNodes { - if peerNodeID == nodeID { - continue - } - - peerIPRange := znet.NodesIPRange[peerNodeID] - allowedIPs := []gridtypes.IPNet{ - peerIPRange, - workloads.WgIP(peerIPRange), - } - - if peerNodeID == znet.PublicNodeID { - allowedIPs = append(allowedIPs, nonAccessibleIPRanges...) - } - - peers = append(peers, zos.Peer{ - Subnet: znet.NodesIPRange[peerNodeID], - WGPublicKey: znet.Keys[peerNodeID].PublicKey().String(), - Endpoint: fmt.Sprintf("%s:%d", endpoints[peerNodeID], znet.WGPort[peerNodeID]), - AllowedIPs: allowedIPs, - }) - } - - if nodeID == znet.PublicNodeID { - // external node - if znet.AddWGAccess { - peers = append(peers, zos.Peer{ - Subnet: *znet.ExternalIP, - WGPublicKey: znet.ExternalSK.PublicKey().String(), - AllowedIPs: []gridtypes.IPNet{*znet.ExternalIP, workloads.WgIP(*znet.ExternalIP)}, - }) - } - - // hidden nodes - for _, peerNodeID := range hiddenNodes { - peerIPRange := znet.NodesIPRange[peerNodeID] - peers = append(peers, zos.Peer{ - Subnet: peerIPRange, - WGPublicKey: znet.Keys[peerNodeID].PublicKey().String(), - AllowedIPs: []gridtypes.IPNet{ - peerIPRange, - workloads.WgIP(peerIPRange), - }, - }) - } - } - - workload := znet.ZosWorkload(znet.NodesIPRange[nodeID], znet.Keys[nodeID].String(), uint16(znet.WGPort[nodeID]), peers, string(metadataBytes), znet.MyceliumKeys[nodeID]) - deployment := workloads.NewGridDeployment(d.tfPluginClient.TwinID, []gridtypes.Workload{workload}) - - // add metadata - deployment.Metadata, err = znet.GenerateMetadata() - if err != nil { - return nil, errors.Wrapf(err, "failed to generate deployment %s metadata", znet.Name) - } - - deployments[nodeID] = deployment - } - - // hidden nodes deployments - for _, nodeID := range hiddenNodes { - peers := make([]zos.Peer, 0) - if znet.PublicNodeID != 0 { - peers = append(peers, zos.Peer{ - WGPublicKey: znet.Keys[znet.PublicNodeID].PublicKey().String(), - Subnet: znet.NodesIPRange[nodeID], - AllowedIPs: []gridtypes.IPNet{ - znet.IPRange, - workloads.IPNet(100, 64, 0, 0, 16), - }, - Endpoint: fmt.Sprintf("%s:%d", endpoints[znet.PublicNodeID], znet.WGPort[znet.PublicNodeID]), - }) - } - workload := znet.ZosWorkload(znet.NodesIPRange[nodeID], znet.Keys[nodeID].String(), uint16(znet.WGPort[nodeID]), peers, string(metadataBytes), znet.MyceliumKeys[nodeID]) - deployment := workloads.NewGridDeployment(d.tfPluginClient.TwinID, []gridtypes.Workload{workload}) - - // add metadata - var err error - deployment.Metadata, err = znet.GenerateMetadata() - if err != nil { - return nil, errors.Wrapf(err, "failed to generate deployment %s metadata", znet.Name) - } - - deployments[nodeID] = deployment - } - return deployments, nil -} - // Deploy deploys the network deployments using the deployer -func (d *NetworkDeployer) Deploy(ctx context.Context, znet *workloads.ZNet) error { - znets, err := d.Validate(ctx, []*workloads.ZNet{znet}) +func (d *NetworkDeployer) Deploy(ctx context.Context, znet workloads.Network) error { + zNets, err := d.Validate(ctx, []workloads.Network{znet}) if err != nil { return err } - nodeDeployments, err := d.GenerateVersionlessDeployments(ctx, znets) + nodeDeployments, err := d.GenerateVersionlessDeployments(ctx, zNets) if err != nil { return errors.Wrap(err, "could not generate deployments data") } - newDeployments := make(map[uint32]gridtypes.Deployment) + + newDeployments := make(map[uint32]zos.Deployment) for node, deployments := range nodeDeployments { if len(deployments) != 1 { // this should never happen @@ -427,40 +109,46 @@ func (d *NetworkDeployer) Deploy(ctx context.Context, znet *workloads.ZNet) erro } newDeploymentsSolutionProvider := make(map[uint32]*uint64) - for _, nodeID := range znet.Nodes { + for _, nodeID := range znet.GetNodes() { // solution providers newDeploymentsSolutionProvider[nodeID] = nil } - oldDeployments := znet.NodeDeploymentID - znet.NodeDeploymentID, err = d.deployer.Deploy(ctx, znet.NodeDeploymentID, newDeployments, newDeploymentsSolutionProvider) + oldDeployments := znet.GetNodeDeploymentID() + nodeDeploymentIDs, err := d.deployer.Deploy(ctx, oldDeployments, newDeployments, newDeploymentsSolutionProvider) + znet.SetNodeDeploymentID(nodeDeploymentIDs) // update deployment and plugin state // error is not returned immediately before updating state because of untracked failed deployments - nodesUsed := znet.Nodes - if znet.PublicNodeID != 0 && !slices.Contains(znet.Nodes, znet.PublicNodeID) { - nodesUsed = append(znet.Nodes, znet.PublicNodeID) + nodesUsed := znet.GetNodes() + if znet.GetPublicNodeID() != 0 && !slices.Contains(znet.GetNodes(), znet.GetPublicNodeID()) { + nodesUsed = append(nodesUsed, znet.GetPublicNodeID()) } for _, nodeID := range nodesUsed { - if contractID, ok := znet.NodeDeploymentID[nodeID]; ok && contractID != 0 { - d.tfPluginClient.State.Networks.UpdateNetworkSubnets(znet.Name, znet.NodesIPRange) + if contractID, ok := znet.GetNodeDeploymentID()[nodeID]; ok && contractID != 0 { + d.tfPluginClient.State.Networks.UpdateNetworkSubnets(znet.GetName(), znet.GetNodesIPRange()) d.tfPluginClient.State.StoreContractIDs(nodeID, contractID) } } for nodeID, contract := range oldDeployments { // public node is removed - if _, ok := znet.NodeDeploymentID[nodeID]; !ok { + if _, ok := znet.GetNodeDeploymentID()[nodeID]; !ok { d.tfPluginClient.State.RemoveContractIDs(nodeID, contract) } } if err != nil { - return errors.Wrapf(err, "could not deploy network %s", znet.Name) + return errors.Wrapf(err, "could not deploy network %s", znet.GetName()) } - if err := d.ReadNodesConfig(ctx, znet); err != nil { + dls, err := d.deployer.GetDeployments(ctx, znet.GetNodeDeploymentID()) + if err != nil { + return errors.Wrap(err, "failed to get deployment objects") + } + + if err := znet.ReadNodesConfig(ctx, dls); err != nil { return errors.Wrap(err, "could not read node's data") } @@ -468,9 +156,9 @@ func (d *NetworkDeployer) Deploy(ctx context.Context, znet *workloads.ZNet) erro } // BatchDeploy deploys multiple network deployments using the deployer -func (d *NetworkDeployer) BatchDeploy(ctx context.Context, znets []*workloads.ZNet, updateMetadata ...bool) error { +func (d *NetworkDeployer) BatchDeploy(ctx context.Context, zNets []workloads.Network, updateMetadata ...bool) error { var multiErr error - filteredZNets, err := d.Validate(ctx, znets) + filteredZNets, err := d.Validate(ctx, zNets) if err != nil { multiErr = multierror.Append(multiErr, err) } @@ -498,9 +186,9 @@ func (d *NetworkDeployer) BatchDeploy(ctx context.Context, znets []*workloads.ZN // update deployment and plugin state // error is not returned immediately before updating state because of untracked failed deployments - for _, znet := range znets { + for _, znet := range zNets { if err := d.updateStateFromDeployments(ctx, znet, newDls, update); err != nil { - return errors.Wrapf(err, "failed to update network '%s' state", znet.Name) + return errors.Wrapf(err, "failed to update network '%s' state", znet.GetName()) } } @@ -508,25 +196,32 @@ func (d *NetworkDeployer) BatchDeploy(ctx context.Context, znets []*workloads.ZN } // Cancel cancels all the deployments -func (d *NetworkDeployer) Cancel(ctx context.Context, znet *workloads.ZNet) error { +func (d *NetworkDeployer) Cancel(ctx context.Context, znet workloads.Network) error { err := validateAccountBalanceForExtrinsics(d.tfPluginClient.SubstrateConn, d.tfPluginClient.Identity) if err != nil { return err } - for nodeID, contractID := range znet.NodeDeploymentID { + for nodeID, contractID := range znet.GetNodeDeploymentID() { err = d.deployer.Cancel(ctx, contractID) if err != nil { - return errors.Wrapf(err, "could not cancel network %s, contract %d", znet.Name, contractID) + return errors.Wrapf(err, "could not cancel network %s, contract %d", znet.GetName(), contractID) } - delete(znet.NodeDeploymentID, nodeID) + znetDeploymentsIDs := znet.GetNodeDeploymentID() + delete(znetDeploymentsIDs, nodeID) + znet.SetNodeDeploymentID(znetDeploymentsIDs) d.tfPluginClient.State.CurrentNodeDeployments[nodeID] = workloads.Delete(d.tfPluginClient.State.CurrentNodeDeployments[nodeID], contractID) } // delete network from state if all contracts was deleted - d.tfPluginClient.State.Networks.DeleteNetwork(znet.Name) + d.tfPluginClient.State.Networks.DeleteNetwork(znet.GetName()) + + dls, err := d.deployer.GetDeployments(ctx, znet.GetNodeDeploymentID()) + if err != nil { + return errors.Wrap(err, "failed to get deployment objects") + } - if err := d.ReadNodesConfig(ctx, znet); err != nil { + if err := znet.ReadNodesConfig(ctx, dls); err != nil { return errors.Wrap(err, "could not read node's data") } @@ -535,10 +230,10 @@ func (d *NetworkDeployer) Cancel(ctx context.Context, znet *workloads.ZNet) erro // BatchCancel cancels all contracts for given networks. if one contracts failed all networks will not be canceled // and state won't be updated. -func (d *NetworkDeployer) BatchCancel(ctx context.Context, znets []*workloads.ZNet) error { +func (d *NetworkDeployer) BatchCancel(ctx context.Context, znets []workloads.Network) error { var contracts []uint64 for _, znet := range znets { - for _, contractID := range znet.NodeDeploymentID { + for _, contractID := range znet.GetNodeDeploymentID() { if contractID != 0 { contracts = append(contracts, contractID) } @@ -549,26 +244,31 @@ func (d *NetworkDeployer) BatchCancel(ctx context.Context, znets []*workloads.ZN return fmt.Errorf("failed to cancel contracts: %w", err) } for _, znet := range znets { - for nodeID, contractID := range znet.NodeDeploymentID { + for nodeID, contractID := range znet.GetNodeDeploymentID() { d.tfPluginClient.State.CurrentNodeDeployments[nodeID] = workloads.Delete(d.tfPluginClient.State.CurrentNodeDeployments[nodeID], contractID) } - d.tfPluginClient.State.Networks.DeleteNetwork(znet.Name) - znet.NodeDeploymentID = make(map[uint32]uint64) - znet.Keys = make(map[uint32]wgtypes.Key) - znet.WGPort = make(map[uint32]int) - znet.NodesIPRange = make(map[uint32]gridtypes.IPNet) - znet.AccessWGConfig = "" + d.tfPluginClient.State.Networks.DeleteNetwork(znet.GetName()) + znet.SetNodeDeploymentID(make(map[uint32]uint64)) + znet.SetKeys(make(map[uint32]wgtypes.Key)) + znet.SetWGPort(make(map[uint32]int)) + znet.SetNodesIPRange(make(map[uint32]zos.IPNet)) + znet.SetAccessWGConfig("") } return nil } -func (d *NetworkDeployer) updateStateFromDeployments(ctx context.Context, znet *workloads.ZNet, dls map[uint32][]gridtypes.Deployment, updateMetadata bool) error { - znet.NodeDeploymentID = map[uint32]uint64{} +func (d *NetworkDeployer) updateStateFromDeployments( + ctx context.Context, + znet workloads.Network, + dls map[uint32][]zos.Deployment, + updateMetadata bool, +) error { + znet.SetNodeDeploymentID(map[uint32]uint64{}) - nodesUsed := znet.Nodes - if znet.PublicNodeID != 0 && !slices.Contains(znet.Nodes, znet.PublicNodeID) { - nodesUsed = append(znet.Nodes, znet.PublicNodeID) + nodesUsed := znet.GetNodes() + if znet.GetPublicNodeID() != 0 && !slices.Contains(znet.GetNodes(), znet.GetPublicNodeID()) { + nodesUsed = append(nodesUsed, znet.GetPublicNodeID()) } for _, nodeID := range nodesUsed { @@ -578,13 +278,13 @@ func (d *NetworkDeployer) updateStateFromDeployments(ctx context.Context, znet * if err != nil { return errors.Wrapf(err, "could not get deployment %d data", dl.ContractID) } - if dlData.Name == znet.Name && dl.ContractID != 0 { - znet.NodeDeploymentID[nodeID] = dl.ContractID + if dlData.Name == znet.GetName() && dl.ContractID != 0 { + znet.GetNodeDeploymentID()[nodeID] = dl.ContractID } } - if contractID, ok := znet.NodeDeploymentID[nodeID]; ok && contractID != 0 { - d.tfPluginClient.State.Networks.UpdateNetworkSubnets(znet.Name, znet.NodesIPRange) + if contractID, ok := znet.GetNodeDeploymentID()[nodeID]; ok && contractID != 0 { + d.tfPluginClient.State.Networks.UpdateNetworkSubnets(znet.GetName(), znet.GetNodesIPRange()) d.tfPluginClient.State.StoreContractIDs(nodeID, contractID) } } @@ -592,95 +292,78 @@ func (d *NetworkDeployer) updateStateFromDeployments(ctx context.Context, znet * if !updateMetadata { return nil } - if err := d.ReadNodesConfig(ctx, znet); err != nil { - return errors.Wrapf(err, "could not read node's data for network %s", znet.Name) + + deployments, err := d.deployer.GetDeployments(ctx, znet.GetNodeDeploymentID()) + if err != nil { + return errors.Wrap(err, "failed to get deployment objects") + } + + if err := znet.ReadNodesConfig(ctx, deployments); err != nil { + return errors.Wrapf(err, "could not read node's data for network %s", znet.GetName()) } return nil } -// InvalidateBrokenAttributes removes outdated attrs and deleted contracts -func (d *NetworkDeployer) InvalidateBrokenAttributes(znet *workloads.ZNet) error { - for node, contractID := range znet.NodeDeploymentID { - contract, err := d.tfPluginClient.SubstrateConn.GetContract(contractID) - if (err == nil && !contract.IsCreated()) || errors.Is(err, substrate.ErrNotFound) { - delete(znet.NodeDeploymentID, node) - delete(znet.NodesIPRange, node) - delete(znet.Keys, node) - delete(znet.WGPort, node) - } else if err != nil { - return errors.Wrapf(err, "could not get node %d contract %d", node, contractID) +func (d *NetworkDeployer) calcPublicNode(ctx context.Context, znets []workloads.Network, endpoints map[uint32]net.IP) (map[uint32]struct{}, uint32, error) { + allNodes := make(map[uint32]struct{}) + var multiErr error + + for _, znet := range znets { + for _, node := range znet.GetNodes() { + allNodes[node] = struct{}{} + } + if znet.GetPublicNodeID() != 0 { + allNodes[znet.GetPublicNodeID()] = struct{}{} } } - if znet.ExternalIP != nil && !znet.IPRange.Contains(znet.ExternalIP.IP) { - znet.ExternalIP = nil - } - for node, ip := range znet.NodesIPRange { - if !znet.IPRange.Contains(ip.IP) { - delete(znet.NodesIPRange, node) + + var publicNode uint32 + var err error + for nodeID := range allNodes { + // we check first that there is a public node in all nodes in the networks. + // that way we can save extra requests to get a new public node + // and its used ports and endpoints. this public node also might not be used + // as access node if there is no need for it. + if endpoints[nodeID] != nil && endpoints[nodeID].To4() != nil { + publicNode = nodeID + break } } - if znet.PublicNodeID != 0 { - // TODO: add a check that the node is still public - cl, err := d.tfPluginClient.NcPool.GetNodeClient(d.tfPluginClient.SubstrateConn, znet.PublicNodeID) + + // if none of the nodes used are public (have ipv4 endpoint) we do + // an extra check that at least one network needs a public node before + // fetching a public node. we can use this one node as an access point + // to all networks that need it. + if publicNode == 0 && needPublicNode(znets) { + publicNode, err = GetPublicNode(ctx, *d.tfPluginClient, nil) + // we don't return immediately here as there might be some + // networks that don't need a public node so they should continue + // processing fine if err != nil { - // whatever the error, delete it and it will get reassigned later - znet.PublicNodeID = 0 - } - if err := cl.IsNodeUp(context.Background()); err != nil { - znet.PublicNodeID = 0 + multiErr = multierror.Append( + multiErr, + fmt.Errorf("failed to get public node: %w", err), + ) } } - if !znet.AddWGAccess { - znet.ExternalIP = nil - } - return nil + return allNodes, publicNode, multiErr } -// ReadNodesConfig reads the configuration of a network -func (d *NetworkDeployer) ReadNodesConfig(ctx context.Context, znet *workloads.ZNet) error { - keys := make(map[uint32]wgtypes.Key) - WGPort := make(map[uint32]int) - nodesIPRange := make(map[uint32]gridtypes.IPNet) - log.Debug().Msg("reading node config") - nodeDeployments, err := d.deployer.GetDeployments(ctx, znet.NodeDeploymentID) - if err != nil { - return errors.Wrap(err, "failed to get deployment objects") - } - - WGAccess := false - for node, dl := range nodeDeployments { - for _, wl := range dl.Workloads { - if wl.Type != zos.NetworkType { - continue - } - data, err := wl.WorkloadData() - if err != nil { - return errors.Wrap(err, "could not parse workload data") - } - - d := data.(*zos.Network) - WGPort[node] = int(d.WGListenPort) - keys[node], err = wgtypes.ParseKey(d.WGPrivateKey) - if err != nil { - return errors.Wrap(err, "could not parse wg private key from workload object") - } - nodesIPRange[node] = d.Subnet - // this will fail when hidden node is supported - for _, peer := range d.Peers { - if peer.Endpoint == "" { - WGAccess = true - } - } +func needPublicNode(znets []workloads.Network) bool { + // entering here means all nodes for all networks are either hidden or ipv6 only + // we need an extra public node in two cases: + // - the user asked for WireGuard access + // - there are multiple nodes in the network and none of them have ipv4. + // because networks must communicate through ipv4 + for _, znet := range znets { + if znet.GetAddWGAccess() { + return true + } + if len(znet.GetNodes()) > 1 { + return true } } - znet.Keys = keys - znet.WGPort = WGPort - znet.NodesIPRange = nodesIPRange - znet.AddWGAccess = WGAccess - if !WGAccess { - znet.AccessWGConfig = "" - } - return nil + return false } diff --git a/grid-client/deployer/network_deployer_test.go b/grid-client/deployer/network_deployer_test.go index b80ea37b2..e94f3c1a9 100644 --- a/grid-client/deployer/network_deployer_test.go +++ b/grid-client/deployer/network_deployer_test.go @@ -13,8 +13,7 @@ import ( client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/state" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" - "github.com/threefoldtech/zos/pkg/gridtypes" - "github.com/threefoldtech/zos/pkg/gridtypes/zos" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) func constructTestNetwork() workloads.ZNet { @@ -22,10 +21,10 @@ func constructTestNetwork() workloads.ZNet { Name: "network", Description: "network for testing", Nodes: []uint32{nodeID}, - IPRange: gridtypes.NewIPNet(net.IPNet{ + IPRange: zosTypes.IPNet{IPNet: net.IPNet{ IP: net.IPv4(10, 1, 0, 0), Mask: net.CIDRMask(16, 32), - }), + }}, AddWGAccess: false, } } @@ -63,11 +62,11 @@ func TestNetworkDeployer(t *testing.T) { d, _, _, _ := constructTestNetworkDeployer(t, tfPluginClient, false) znet.IPRange.Mask = net.CIDRMask(20, 32) - _, err := d.Validate(context.Background(), []*workloads.ZNet{&znet}) + _, err := d.Validate(context.Background(), []workloads.Network{&znet}) assert.Error(t, err) znet.IPRange.Mask = net.CIDRMask(16, 32) - _, err = d.Validate(context.Background(), []*workloads.ZNet{&znet}) + _, err = d.Validate(context.Background(), []workloads.Network{&znet}) assert.NoError(t, err) }) @@ -77,11 +76,11 @@ func TestNetworkDeployer(t *testing.T) { d, _, _, _ := constructTestNetworkDeployer(t, tfPluginClient, false) znet.IPRange.Mask = net.CIDRMask(20, 32) - _, err := d.Validate(context.Background(), []*workloads.ZNet{&znet}) + _, err := d.Validate(context.Background(), []workloads.Network{&znet}) assert.Error(t, err) znet.IPRange.Mask = net.CIDRMask(16, 32) - _, err = d.Validate(context.Background(), []*workloads.ZNet{&znet}) + _, err = d.Validate(context.Background(), []workloads.Network{&znet}) assert.Error(t, err) }) @@ -111,7 +110,7 @@ func TestNetworkDeployer(t *testing.T) { Return(client.NewNodeClient(twinID, cl, d.tfPluginClient.RMBTimeout), nil). AnyTimes() - dls, err := d.GenerateVersionlessDeployments(context.Background(), []*workloads.ZNet{&znet}) + dls, err := d.GenerateVersionlessDeployments(context.Background(), []workloads.Network{&znet}) assert.NoError(t, err) externalIP := "" @@ -120,7 +119,7 @@ func TestNetworkDeployer(t *testing.T) { } metadata, err := json.Marshal(workloads.NetworkMetaData{ - Version: workloads.Version, + Version: int(workloads.Version3), UserAccesses: []workloads.UserAccess{ { Subnet: externalIP, @@ -131,14 +130,14 @@ func TestNetworkDeployer(t *testing.T) { }) assert.NoError(t, err) - workload := znet.ZosWorkload(znet.NodesIPRange[nodeID], znet.Keys[nodeID].String(), uint16(znet.WGPort[nodeID]), []zos.Peer{}, string(metadata), nil) - networkDl := workloads.NewGridDeployment(twinID, []gridtypes.Workload{workload}) + workload := znet.ZosWorkload(znet.NodesIPRange[nodeID], znet.Keys[nodeID].String(), uint16(znet.WGPort[nodeID]), []zosTypes.Peer{}, string(metadata), nil) + networkDl := workloads.NewGridDeployment(twinID, 0, []zosTypes.Workload{workload}) networkDl.Metadata = "{\"version\":3,\"type\":\"network\",\"name\":\"network\",\"projectName\":\"Network\"}" assert.Equal(t, len(networkDl.Workloads), len(dls[znet.Nodes[0]][0].Workloads)) assert.Equal(t, networkDl.Workloads, dls[znet.Nodes[0]][0].Workloads) - assert.Equal(t, dls, map[uint32][]gridtypes.Deployment{ + assert.Equal(t, dls, map[uint32][]zosTypes.Deployment{ nodeID: {networkDl}, }) }) @@ -147,15 +146,15 @@ func TestNetworkDeployer(t *testing.T) { func TestNetworkBatchCancel(t *testing.T) { tfPluginClient, err := setup() assert.NoError(t, err) - networks := []*workloads.ZNet{ - { + networks := []workloads.Network{ + &workloads.ZNet{ NodeDeploymentID: map[uint32]uint64{ 1: 100, 2: 200, }, Nodes: []uint32{1}, }, - { + &workloads.ZNet{ NodeDeploymentID: map[uint32]uint64{ 1: 101, 2: 201, @@ -174,8 +173,8 @@ func TestNetworkBatchCancel(t *testing.T) { ).Return(nil) err = net.BatchCancel(context.Background(), networks) assert.NoError(t, err) - assert.Len(t, networks[0].NodeDeploymentID, 0) - assert.Len(t, networks[1].NodeDeploymentID, 0) + assert.Len(t, networks[0].GetNodeDeploymentID(), 0) + assert.Len(t, networks[1].GetNodeDeploymentID(), 0) assert.Len(t, tfPluginClient.State.CurrentNodeDeployments[1], 1) assert.Len(t, tfPluginClient.State.CurrentNodeDeployments[2], 0) } @@ -195,10 +194,10 @@ func ExampleNetworkDeployer_Deploy() { Name: "network", Description: "network for testing", Nodes: []uint32{nodeID}, - IPRange: gridtypes.NewIPNet(net.IPNet{ + IPRange: zosTypes.IPNet{IPNet: net.IPNet{ IP: net.IPv4(10, 1, 0, 0), Mask: net.CIDRMask(16, 32), - }), + }}, AddWGAccess: false, } @@ -226,24 +225,24 @@ func ExampleNetworkDeployer_BatchDeploy() { Name: "network1", Description: "network for testing", Nodes: []uint32{nodeID}, - IPRange: gridtypes.NewIPNet(net.IPNet{ + IPRange: zosTypes.IPNet{IPNet: net.IPNet{ IP: net.IPv4(10, 1, 0, 0), Mask: net.CIDRMask(16, 32), - }), + }}, AddWGAccess: false, } n2 := workloads.ZNet{ Name: "network2", Description: "network for testing", Nodes: []uint32{nodeID}, - IPRange: gridtypes.NewIPNet(net.IPNet{ + IPRange: zosTypes.IPNet{IPNet: net.IPNet{ IP: net.IPv4(10, 1, 0, 0), Mask: net.CIDRMask(16, 32), - }), + }}, AddWGAccess: false, } - err = tfPluginClient.NetworkDeployer.BatchDeploy(context.Background(), []*workloads.ZNet{&n1, &n2}) + err = tfPluginClient.NetworkDeployer.BatchDeploy(context.Background(), []workloads.Network{&n1, &n2}) if err != nil { fmt.Println(err) return diff --git a/grid-client/go.mod b/grid-client/go.mod index df1e2a5f7..8c0ea2498 100644 --- a/grid-client/go.mod +++ b/grid-client/go.mod @@ -17,6 +17,7 @@ require ( github.com/threefoldtech/tfgrid-sdk-go/grid-proxy v0.15.18 github.com/threefoldtech/tfgrid-sdk-go/rmb-sdk-go v0.15.18 github.com/threefoldtech/zos v0.5.6-0.20240902110349-172a0a29a6ee + github.com/threefoldtech/zos4 v0.5.6-0.20241008102757-02d898c580c4 github.com/vedhavyas/go-subkey v1.0.3 golang.org/x/crypto v0.27.0 golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa diff --git a/grid-client/go.sum b/grid-client/go.sum index 1b6204cfa..5672c84c6 100644 --- a/grid-client/go.sum +++ b/grid-client/go.sum @@ -122,6 +122,8 @@ github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20240827163226 github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20240827163226-d4e15e206974/go.mod h1:dtDKAPiUDxAwIkfHV7xcAFZcOm+xwNIuOI1MLFS+MeQ= github.com/threefoldtech/zos v0.5.6-0.20240902110349-172a0a29a6ee h1:pqpYVM0qkXujplHNfH6w5GDqcY5sLJAgOc4/hlR6+Xw= github.com/threefoldtech/zos v0.5.6-0.20240902110349-172a0a29a6ee/go.mod h1:lut72yYMJhgK0QRvF0Wd/mB3+OfIoXWz04DQuXck3Sw= +github.com/threefoldtech/zos4 v0.5.6-0.20241008102757-02d898c580c4 h1:JCExxpPL32G7evO/+gHwlZLfAX1+l9QN9t55tnPDCp0= +github.com/threefoldtech/zos4 v0.5.6-0.20241008102757-02d898c580c4/go.mod h1:7KFtZaCcEFwQ1/cz/+hkYK616Ww04ISZgmMqLWHz6To= github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= diff --git a/grid-client/graphql/contracts.go b/grid-client/graphql/contracts.go index 9888b271c..f5ca9504d 100644 --- a/grid-client/graphql/contracts.go +++ b/grid-client/graphql/contracts.go @@ -13,8 +13,7 @@ import ( client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/subi" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" - "github.com/threefoldtech/zos/pkg/gridtypes" - "github.com/threefoldtech/zos/pkg/gridtypes/zos" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) // ContractsGetter for contracts getter from graphql @@ -188,11 +187,11 @@ func (c *ContractsGetter) GetNodeContractsByTypeAndName(projectName, deploymentT } // filterNameContracts returns the name contracts of the given name gateways -func (c *ContractsGetter) filterNameContracts(nameContracts []Contract, nameGatewayWorkloads []gridtypes.Workload) []Contract { +func (c *ContractsGetter) filterNameContracts(nameContracts []Contract, nameGatewayWorkloads []zos.Workload) []Contract { filteredNameContracts := make([]Contract, 0) for _, contract := range nameContracts { for _, w := range nameGatewayWorkloads { - if w.Name.String() == contract.Name { + if w.Name == contract.Name { filteredNameContracts = append(filteredNameContracts, contract) } } @@ -201,22 +200,22 @@ func (c *ContractsGetter) filterNameContracts(nameContracts []Contract, nameGate return filteredNameContracts } -func (c *ContractsGetter) filterNameGatewaysWithinNodeContracts(nodeContracts []Contract) ([]gridtypes.Workload, error) { - nameGatewayWorkloads := make([]gridtypes.Workload, 0) +func (c *ContractsGetter) filterNameGatewaysWithinNodeContracts(nodeContracts []Contract) ([]zos.Workload, error) { + nameGatewayWorkloads := make([]zos.Workload, 0) for _, contract := range nodeContracts { nodeClient, err := c.ncPool.GetNodeClient(c.substrateConn, contract.NodeID) if err != nil { - return []gridtypes.Workload{}, errors.Wrapf(err, "could not get node client: %d", contract.NodeID) + return []zos.Workload{}, errors.Wrapf(err, "could not get node client: %d", contract.NodeID) } contractID, err := strconv.Atoi(contract.ContractID) if err != nil { - return []gridtypes.Workload{}, errors.Wrapf(err, "could not parse contract id: %s", contract.ContractID) + return []zos.Workload{}, errors.Wrapf(err, "could not parse contract id: %s", contract.ContractID) } dl, err := nodeClient.DeploymentGet(context.Background(), uint64(contractID)) if err != nil { - return []gridtypes.Workload{}, errors.Wrapf(err, "could not get deployment %d from node %d", contractID, contract.NodeID) + return []zos.Workload{}, errors.Wrapf(err, "could not get deployment %d from node %d", contractID, contract.NodeID) } for _, workload := range dl.Workloads { diff --git a/grid-client/integration_tests/batch_gateway_name_test.go b/grid-client/integration_tests/batch_gateway_name_test.go index 12b29be63..c300e20aa 100644 --- a/grid-client/integration_tests/batch_gateway_name_test.go +++ b/grid-client/integration_tests/batch_gateway_name_test.go @@ -67,7 +67,7 @@ func TestBatchGatewayNameDeployment(t *testing.T) { require.NoError(t, err) }) - dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID1, "", nil, network.Name, nil, nil, []workloads.VM{vm}, nil, nil) + dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID1, "", nil, network.Name, nil, nil, []workloads.VM{vm}, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &dl) require.NoError(t, err) diff --git a/grid-client/integration_tests/batch_vm_test.go b/grid-client/integration_tests/batch_vm_test.go index c428478b3..a4a7cdfc5 100644 --- a/grid-client/integration_tests/batch_vm_test.go +++ b/grid-client/integration_tests/batch_vm_test.go @@ -67,7 +67,7 @@ func TestBatchVMDeployment(t *testing.T) { }, } - err = tfPluginClient.NetworkDeployer.BatchDeploy(context.Background(), []*workloads.ZNet{&network1, &network2}) + err = tfPluginClient.NetworkDeployer.BatchDeploy(context.Background(), []workloads.Network{&network1, &network2}) require.NoError(t, err) t.Cleanup(func() { @@ -78,8 +78,8 @@ func TestBatchVMDeployment(t *testing.T) { require.NoError(t, err) }) - dl1 := workloads.NewDeployment(fmt.Sprintf("dl1_%s", generateRandString(10)), nodeID1, "", nil, network1.Name, nil, nil, []workloads.VM{vm1}, nil, nil) - dl2 := workloads.NewDeployment(fmt.Sprintf("dl2_%s", generateRandString(10)), nodeID2, "", nil, network2.Name, nil, nil, []workloads.VM{vm2}, nil, nil) + dl1 := workloads.NewDeployment(fmt.Sprintf("dl1_%s", generateRandString(10)), nodeID1, "", nil, network1.Name, nil, nil, []workloads.VM{vm1}, nil, nil, nil) + dl2 := workloads.NewDeployment(fmt.Sprintf("dl2_%s", generateRandString(10)), nodeID2, "", nil, network2.Name, nil, nil, []workloads.VM{vm2}, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.BatchDeploy(context.Background(), []*workloads.Deployment{&dl1, &dl2}) require.NoError(t, err) diff --git a/grid-client/integration_tests/disk_test.go b/grid-client/integration_tests/disk_test.go index cad83cf1c..0de28501b 100644 --- a/grid-client/integration_tests/disk_test.go +++ b/grid-client/integration_tests/disk_test.go @@ -38,7 +38,7 @@ func TestDiskDeployment(t *testing.T) { Description: "disk test", } - dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, "", []workloads.Disk{disk}, nil, nil, nil, nil) + dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, "", []workloads.Disk{disk}, nil, nil, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &dl) require.NoError(t, err) diff --git a/grid-client/integration_tests/gateway_name_test.go b/grid-client/integration_tests/gateway_name_test.go index a97033672..3145fa8dd 100644 --- a/grid-client/integration_tests/gateway_name_test.go +++ b/grid-client/integration_tests/gateway_name_test.go @@ -80,7 +80,7 @@ func TestGatewayNameDeployment(t *testing.T) { require.NoError(t, err) }) - dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm}, nil, nil) + dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm}, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &dl) require.NoError(t, err) diff --git a/grid-client/integration_tests/gatway_fqdn_test.go b/grid-client/integration_tests/gatway_fqdn_test.go index 254218610..71af578cd 100644 --- a/grid-client/integration_tests/gatway_fqdn_test.go +++ b/grid-client/integration_tests/gatway_fqdn_test.go @@ -71,7 +71,7 @@ func TestGatewayFQDNDeployment(t *testing.T) { require.NoError(t, err) }) - dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm}, nil, nil) + dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm}, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &dl) require.NoError(t, err) diff --git a/grid-client/integration_tests/network_test.go b/grid-client/integration_tests/network_test.go index 3872366f4..65f2eb7a6 100644 --- a/grid-client/integration_tests/network_test.go +++ b/grid-client/integration_tests/network_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/deployer" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" - "github.com/threefoldtech/zos/pkg/gridtypes" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) func TestNetworkDeployment(t *testing.T) { @@ -31,10 +31,10 @@ func TestNetworkDeployment(t *testing.T) { Name: fmt.Sprintf("net_%s", generateRandString(10)), Description: "not skynet", Nodes: []uint32{nodeID1}, - IPRange: gridtypes.NewIPNet(net.IPNet{ + IPRange: zos.IPNet{IPNet: net.IPNet{ IP: net.IPv4(10, 1, 0, 0), Mask: net.CIDRMask(16, 32), - }), + }}, AddWGAccess: true, } diff --git a/grid-client/integration_tests/node_filter.go b/grid-client/integration_tests/node_filter.go index cba6da868..ef53665f6 100644 --- a/grid-client/integration_tests/node_filter.go +++ b/grid-client/integration_tests/node_filter.go @@ -21,10 +21,17 @@ type nodeFilterCfg struct { domain bool hasGPU bool rentedBy uint64 + features []string } type nodeFilterOpts func(*nodeFilterCfg) +func WithFeatures(features []string) nodeFilterOpts { + return func(p *nodeFilterCfg) { + p.features = features + } +} + func WithFreeSRU(sruGB uint64) nodeFilterOpts { return func(p *nodeFilterCfg) { p.freeSRU = sruGB @@ -89,9 +96,10 @@ func generateNodeFilter(opts ...nodeFilterOpts) types.NodeFilter { } nodeFilter := types.NodeFilter{ - FarmIDs: []uint64{1}, // freefarm is used in tests - Status: []string{"up"}, - FreeSRU: convertGBToBytes(cfg.freeSRU + minRootfs), + FarmIDs: []uint64{1}, // freefarm is used in tests + Status: []string{"up"}, + FreeSRU: convertGBToBytes(cfg.freeSRU + minRootfs), + Features: cfg.features, } if cfg.freeHRU > 0 { diff --git a/grid-client/integration_tests/qsfs_test.go b/grid-client/integration_tests/qsfs_test.go index 80e099474..b217a9748 100644 --- a/grid-client/integration_tests/qsfs_test.go +++ b/grid-client/integration_tests/qsfs_test.go @@ -82,7 +82,7 @@ func TestQSFSDeployment(t *testing.T) { metaZDBs = append(metaZDBs, zdb) } - dl1 := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, "", nil, append(dataZDBs, metaZDBs...), nil, nil, nil) + dl1 := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, "", nil, append(dataZDBs, metaZDBs...), nil, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &dl1) require.NoError(t, err) @@ -174,7 +174,7 @@ func TestQSFSDeployment(t *testing.T) { require.NoError(t, err) }) - dl2 := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, append(dataZDBs, metaZDBs...), []workloads.VM{vm}, []workloads.QSFS{qsfs}, nil) + dl2 := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, append(dataZDBs, metaZDBs...), []workloads.VM{vm}, nil, []workloads.QSFS{qsfs}, nil) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &dl2) require.NoError(t, err) diff --git a/grid-client/integration_tests/setup.go b/grid-client/integration_tests/setup.go index 55c9da403..47b1240c4 100644 --- a/grid-client/integration_tests/setup.go +++ b/grid-client/integration_tests/setup.go @@ -16,7 +16,7 @@ import ( "github.com/pkg/errors" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/deployer" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" - "github.com/threefoldtech/zos/pkg/gridtypes" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "golang.org/x/crypto/ssh" ) @@ -35,10 +35,10 @@ func generateBasicNetwork(nodeIDs []uint32) workloads.ZNet { Name: fmt.Sprintf("net_%s", generateRandString(10)), Description: "network for testing", Nodes: nodeIDs, - IPRange: gridtypes.NewIPNet(net.IPNet{ + IPRange: zos.IPNet{IPNet: net.IPNet{ IP: net.IPv4(10, 20, 0, 0), Mask: net.CIDRMask(16, 32), - }), + }}, } } diff --git a/grid-client/integration_tests/two_vms_same_network_test.go b/grid-client/integration_tests/two_vms_same_network_test.go index 5730b3407..3816213ad 100644 --- a/grid-client/integration_tests/two_vms_same_network_test.go +++ b/grid-client/integration_tests/two_vms_same_network_test.go @@ -78,7 +78,7 @@ func TestTwoVMsSameNetworkWithPublicIPV6(t *testing.T) { require.NoError(t, err) }) - dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm1, vm2}, nil, nil) + dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm1, vm2}, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &dl) require.NoError(t, err) diff --git a/grid-client/integration_tests/vm_disk_test.go b/grid-client/integration_tests/vm_disk_test.go index 3f03689d9..300424a45 100644 --- a/grid-client/integration_tests/vm_disk_test.go +++ b/grid-client/integration_tests/vm_disk_test.go @@ -74,7 +74,7 @@ func TestVMWithTwoDisk(t *testing.T) { require.NoError(t, err) }) - dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, []workloads.Disk{disk1, disk2}, nil, []workloads.VM{vm}, nil, nil) + dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, []workloads.Disk{disk1, disk2}, nil, []workloads.VM{vm}, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &dl) require.NoError(t, err) diff --git a/grid-client/integration_tests/vm_gpu_test.go b/grid-client/integration_tests/vm_gpu_test.go index 373ed8c5f..61e31ef9a 100644 --- a/grid-client/integration_tests/vm_gpu_test.go +++ b/grid-client/integration_tests/vm_gpu_test.go @@ -11,7 +11,7 @@ import ( "github.com/threefoldtech/tfgrid-sdk-go/grid-client/deployer" node "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" - "github.com/threefoldtech/zos/pkg/gridtypes/zos" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) func ConvertGPUsToStr(gpus []node.GPU) (zosGPUs []zos.GPU) { @@ -86,7 +86,7 @@ func TestVMWithGPUDeployment(t *testing.T) { require.NoError(t, err) }) - dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, []workloads.Disk{disk}, nil, []workloads.VM{vm}, nil, nil) + dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, []workloads.Disk{disk}, nil, []workloads.VM{vm}, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &dl) require.NoError(t, err) diff --git a/grid-client/integration_tests/vm_light_test.go b/grid-client/integration_tests/vm_light_test.go new file mode 100644 index 000000000..b63606218 --- /dev/null +++ b/grid-client/integration_tests/vm_light_test.go @@ -0,0 +1,160 @@ +// Package integration for integration tests +package integration + +import ( + "context" + "fmt" + "net" + "strconv" + "testing" + + "github.com/stretchr/testify/require" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/deployer" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" +) + +func TestVMWLight(t *testing.T) { + tfPluginClient, err := setup() + if err != nil { + t.Skipf("plugin creation failed: %v", err) + } + + publicKey, privateKey, err := GenerateSSHKeyPair() + require.NoError(t, err) + + nodes, err := deployer.FilterNodes( + context.Background(), + tfPluginClient, + generateNodeFilter(WithFreeSRU(3), WithFeatures([]string{zos.NetworkLightType, zos.ZMachineLightType})), + []uint64{*convertGBToBytes(2), *convertGBToBytes(1)}, + nil, + []uint64{*convertGBToBytes(minRootfs)}, + 1, + ) + if err != nil { + t.Skipf("no available nodes found: %v", err) + } + + nodeID := uint32(nodes[0].NodeID) + + myceliumKey, err := workloads.RandomMyceliumKey() + require.NoError(t, err) + + network := workloads.ZNetLight{ + Name: fmt.Sprintf("net_%s", generateRandString(10)), + Description: "network for testing", + Nodes: []uint32{nodeID}, + IPRange: zos.IPNet{IPNet: net.IPNet{ + IP: net.IPv4(10, 20, 0, 0), + Mask: net.CIDRMask(16, 32), + }}, + MyceliumKeys: map[uint32][]byte{nodeID: myceliumKey}, + } + + disk1 := workloads.Disk{ + Name: "diskTest1", + SizeGB: 1, + } + disk2 := workloads.Disk{ + Name: "diskTest2", + SizeGB: 2, + } + + myceliumIPSeed, err := workloads.RandomMyceliumIPSeed() + require.NoError(t, err) + + vm := workloads.VMLight{ + Name: "vm", + NodeID: nodeID, + NetworkName: network.Name, + CPU: minCPU, + MemoryMB: minMemory * 1024, + RootfsSizeMB: minRootfs * 1024, + MyceliumIPSeed: myceliumIPSeed, + Flist: "https://hub.grid.tf/tf-official-apps/base:latest.flist", + Entrypoint: "/sbin/zinit init", + EnvVars: map[string]string{ + "SSH_KEY": publicKey, + }, + Mounts: []workloads.Mount{ + {Name: disk1.Name, MountPoint: "/disk1"}, + {Name: disk2.Name, MountPoint: "/disk2"}, + }, + } + + err = tfPluginClient.NetworkDeployer.Deploy(context.Background(), &network) + require.NoError(t, err) + + t.Cleanup(func() { + err = tfPluginClient.NetworkDeployer.Cancel(context.Background(), &network) + require.NoError(t, err) + }) + + dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, []workloads.Disk{disk1, disk2}, nil, nil, []workloads.VMLight{vm}, nil, nil) + err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &dl) + require.NoError(t, err) + + t.Cleanup(func() { + err = tfPluginClient.DeploymentDeployer.Cancel(context.Background(), &dl) + require.NoError(t, err) + }) + + v, err := tfPluginClient.State.LoadVMLightFromGrid(context.Background(), nodeID, vm.Name, dl.Name) + require.NoError(t, err) + + resDisk1, err := tfPluginClient.State.LoadDiskFromGrid(context.Background(), nodeID, disk1.Name, dl.Name) + require.NoError(t, err) + require.Equal(t, disk1, resDisk1) + + resDisk2, err := tfPluginClient.State.LoadDiskFromGrid(context.Background(), nodeID, disk2.Name, dl.Name) + require.NoError(t, err) + require.Equal(t, disk2, resDisk2) + + // Check that disk has been mounted successfully + output, err := RemoteRun("root", v.MyceliumIP, "df -h | grep -w /disk1", privateKey) + require.NoError(t, err) + require.Contains(t, output, fmt.Sprintf("%d.0G", disk1.SizeGB)) + + output, err = RemoteRun("root", v.MyceliumIP, "df -h | grep -w /disk2", privateKey) + require.NoError(t, err) + require.Contains(t, output, fmt.Sprintf("%d.0G", disk2.SizeGB)) + + // create file -> d1, check file size, move file -> d2, check file size + + _, err = RemoteRun("root", v.MyceliumIP, "dd if=/dev/vda bs=1M count=512 of=/disk1/test.txt", privateKey) + require.NoError(t, err) + + res, err := RemoteRun("root", v.MyceliumIP, "du /disk1/test.txt | head -n1 | awk '{print $1;}' | tr -d -c 0-9", privateKey) + require.NoError(t, err) + require.Equal(t, res, strconv.Itoa(512*1024)) + + _, err = RemoteRun("root", v.MyceliumIP, "mv /disk1/test.txt /disk2/", privateKey) + require.NoError(t, err) + + res, err = RemoteRun("root", v.MyceliumIP, "du /disk2/test.txt | head -n1 | awk '{print $1;}' | tr -d -c 0-9", privateKey) + require.NoError(t, err) + require.Equal(t, res, strconv.Itoa(512*1024)) + + // create file -> d2, check file size, copy file -> d1, check file size + + _, err = RemoteRun("root", v.MyceliumIP, "dd if=/dev/vdb bs=1M count=512 of=/disk2/test.txt", privateKey) + require.NoError(t, err) + + res, err = RemoteRun("root", v.MyceliumIP, "du /disk2/test.txt | head -n1 | awk '{print $1;}' | tr -d -c 0-9", privateKey) + require.NoError(t, err) + require.Equal(t, res, strconv.Itoa(512*1024)) + + _, err = RemoteRun("root", v.MyceliumIP, "cp /disk2/test.txt /disk1/", privateKey) + require.NoError(t, err) + + res, err = RemoteRun("root", v.MyceliumIP, "du /disk1/test.txt | head -n1 | awk '{print $1;}' | tr -d -c 0-9", privateKey) + require.NoError(t, err) + require.Equal(t, res, strconv.Itoa(512*1024)) + + // copy same file -> d1 (not enough space) + + _, err = RemoteRun("root", v.MyceliumIP, "cp /disk2/test.txt /disk1/test2.txt", privateKey) + require.Error(t, err) + require.Contains(t, err.Error(), "No space left on device") +} diff --git a/grid-client/integration_tests/vm_public_ip_test.go b/grid-client/integration_tests/vm_public_ip_test.go index d67705d62..7dc536af2 100644 --- a/grid-client/integration_tests/vm_public_ip_test.go +++ b/grid-client/integration_tests/vm_public_ip_test.go @@ -63,7 +63,7 @@ func TestVMDeployment(t *testing.T) { require.NoError(t, err) }) - dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm}, nil, nil) + dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm}, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &dl) require.NoError(t, err) diff --git a/grid-client/integration_tests/vm_volume_test.go b/grid-client/integration_tests/vm_volume_test.go index cf5684826..331116d39 100644 --- a/grid-client/integration_tests/vm_volume_test.go +++ b/grid-client/integration_tests/vm_volume_test.go @@ -68,7 +68,7 @@ func TestVMWithVolume(t *testing.T) { require.NoError(t, err) }) - dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm}, nil, []workloads.Volume{volume}) + dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm}, nil, nil, []workloads.Volume{volume}) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &dl) require.NoError(t, err) diff --git a/grid-client/integration_tests/vms_private_ips_test.go b/grid-client/integration_tests/vms_private_ips_test.go index 0294cfb0f..4c3d0ba0f 100644 --- a/grid-client/integration_tests/vms_private_ips_test.go +++ b/grid-client/integration_tests/vms_private_ips_test.go @@ -63,7 +63,7 @@ func TestDeploymentsDeploy(t *testing.T) { } }) - d1 := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm1}, nil, nil) + d1 := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm1}, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &d1) if err != nil { t.Fatal(err) @@ -76,7 +76,7 @@ func TestDeploymentsDeploy(t *testing.T) { } }) - d2 := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm1}, nil, nil) + d2 := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm1}, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &d2) if err != nil { t.Fatal(err) @@ -183,15 +183,15 @@ func TestDeploymentsBatchDeploy(t *testing.T) { } }) - d1 := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID1, "", nil, network.Name, nil, nil, []workloads.VM{vm1, vm1, vm1}, nil, nil) + d1 := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID1, "", nil, network.Name, nil, nil, []workloads.VM{vm1, vm1, vm1}, nil, nil, nil) d1.Vms[1].Name = vm2Name d1.Vms[2].Name = vm3Name - d2 := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID1, "", nil, network.Name, nil, nil, []workloads.VM{vm1, vm1, vm1}, nil, nil) + d2 := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID1, "", nil, network.Name, nil, nil, []workloads.VM{vm1, vm1, vm1}, nil, nil, nil) d2.Vms[1].Name = vm2Name d2.Vms[2].Name = vm3Name - d3 := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID2, "", nil, network.Name, nil, nil, []workloads.VM{vm1, vm1, vm1}, nil, nil) + d3 := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID2, "", nil, network.Name, nil, nil, []workloads.VM{vm1, vm1, vm1}, nil, nil, nil) d3.Vms[1].Name = vm2Name d3.Vms[2].Name = vm3Name d3.Vms[0].NodeID = nodeID2 diff --git a/grid-client/integration_tests/wireguard_test.go b/grid-client/integration_tests/wireguard_test.go index 96db06ace..11c96096d 100644 --- a/grid-client/integration_tests/wireguard_test.go +++ b/grid-client/integration_tests/wireguard_test.go @@ -66,7 +66,7 @@ func TestWG(t *testing.T) { require.NoError(t, err) }) - dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm}, nil, nil) + dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm}, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &dl) require.NoError(t, err) diff --git a/grid-client/integration_tests/zdb_test.go b/grid-client/integration_tests/zdb_test.go index a13d65855..38c3e5414 100644 --- a/grid-client/integration_tests/zdb_test.go +++ b/grid-client/integration_tests/zdb_test.go @@ -49,7 +49,7 @@ func TestZDBDeployment(t *testing.T) { Mode: zos.ZDBModeUser, } - dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, "", nil, []workloads.ZDB{zdb}, nil, nil, nil) + dl := workloads.NewDeployment(fmt.Sprintf("dl_%s", generateRandString(10)), nodeID, "", nil, "", nil, []workloads.ZDB{zdb}, nil, nil, nil, nil) err = tfPluginClient.DeploymentDeployer.Deploy(context.Background(), &dl) require.NoError(t, err) diff --git a/grid-client/mocks/deployer_mock.go b/grid-client/mocks/deployer_mock.go index ceea19b18..e27976096 100644 --- a/grid-client/mocks/deployer_mock.go +++ b/grid-client/mocks/deployer_mock.go @@ -9,7 +9,7 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" - gridtypes "github.com/threefoldtech/zos/pkg/gridtypes" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) // MockDeployer is a mock of MockDeployer interface. @@ -36,10 +36,10 @@ func (m *MockDeployer) EXPECT() *MockDeployerMockRecorder { } // BatchDeploy mocks base method. -func (m *MockDeployer) BatchDeploy(ctx context.Context, deployments map[uint32][]gridtypes.Deployment, deploymentsSolutionProvider map[uint32][]*uint64) (map[uint32][]gridtypes.Deployment, error) { +func (m *MockDeployer) BatchDeploy(ctx context.Context, deployments map[uint32][]zosTypes.Deployment, deploymentsSolutionProvider map[uint32][]*uint64) (map[uint32][]zosTypes.Deployment, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "BatchDeploy", ctx, deployments, deploymentsSolutionProvider) - ret0, _ := ret[0].(map[uint32][]gridtypes.Deployment) + ret0, _ := ret[0].(map[uint32][]zosTypes.Deployment) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -65,7 +65,7 @@ func (mr *MockDeployerMockRecorder) Cancel(ctx, contractID interface{}) *gomock. } // Deploy mocks base method. -func (m *MockDeployer) Deploy(ctx context.Context, oldDeploymentIDs map[uint32]uint64, newDeployments map[uint32]gridtypes.Deployment, newDeploymentSolutionProvider map[uint32]*uint64) (map[uint32]uint64, error) { +func (m *MockDeployer) Deploy(ctx context.Context, oldDeploymentIDs map[uint32]uint64, newDeployments map[uint32]zosTypes.Deployment, newDeploymentSolutionProvider map[uint32]*uint64) (map[uint32]uint64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Deploy", ctx, oldDeploymentIDs, newDeployments, newDeploymentSolutionProvider) ret0, _ := ret[0].(map[uint32]uint64) @@ -80,10 +80,10 @@ func (mr *MockDeployerMockRecorder) Deploy(ctx, oldDeploymentIDs, newDeployments } // GetDeployments mocks base method. -func (m *MockDeployer) GetDeployments(ctx context.Context, dls map[uint32]uint64) (map[uint32]gridtypes.Deployment, error) { +func (m *MockDeployer) GetDeployments(ctx context.Context, dls map[uint32]uint64) (map[uint32]zosTypes.Deployment, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetDeployments", ctx, dls) - ret0, _ := ret[0].(map[uint32]gridtypes.Deployment) + ret0, _ := ret[0].(map[uint32]zosTypes.Deployment) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/grid-client/node/node.go b/grid-client/node/node.go index e5c634a18..9aff7a8cf 100644 --- a/grid-client/node/node.go +++ b/grid-client/node/node.go @@ -81,6 +81,7 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog/log" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/subi" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "github.com/threefoldtech/tfgrid-sdk-go/rmb-sdk-go" "github.com/threefoldtech/zos/pkg/capacity/dmi" "github.com/threefoldtech/zos/pkg/gridtypes" @@ -161,7 +162,7 @@ func NewNodeClient(nodeTwin uint32, bus rmb.Client, timeout time.Duration) *Node } // DeploymentDeploy sends the deployment to the node for processing. -func (n *NodeClient) DeploymentDeploy(ctx context.Context, dl gridtypes.Deployment) error { +func (n *NodeClient) DeploymentDeploy(ctx context.Context, dl zosTypes.Deployment) error { ctx, cancel := context.WithTimeout(ctx, n.timeout) defer cancel() @@ -171,7 +172,7 @@ func (n *NodeClient) DeploymentDeploy(ctx context.Context, dl gridtypes.Deployme // DeploymentUpdate update the given deployment. deployment must be a valid update for // a deployment that has been already created via DeploymentDeploy -func (n *NodeClient) DeploymentUpdate(ctx context.Context, dl gridtypes.Deployment) error { +func (n *NodeClient) DeploymentUpdate(ctx context.Context, dl zosTypes.Deployment) error { ctx, cancel := context.WithTimeout(ctx, n.timeout) defer cancel() @@ -180,7 +181,7 @@ func (n *NodeClient) DeploymentUpdate(ctx context.Context, dl gridtypes.Deployme } // DeploymentGet gets a deployment via contract ID -func (n *NodeClient) DeploymentGet(ctx context.Context, contractID uint64) (dl gridtypes.Deployment, err error) { +func (n *NodeClient) DeploymentGet(ctx context.Context, contractID uint64) (dl zosTypes.Deployment, err error) { ctx, cancel := context.WithTimeout(ctx, n.timeout) defer cancel() @@ -211,7 +212,7 @@ func (n *NodeClient) DeploymentDelete(ctx context.Context, contractID uint64) er } // DeploymentList gets all deployments for a twin -func (n *NodeClient) DeploymentList(ctx context.Context) (dls []gridtypes.Deployment, err error) { +func (n *NodeClient) DeploymentList(ctx context.Context) (dls []zosTypes.Deployment, err error) { ctx, cancel := context.WithTimeout(ctx, n.timeout) defer cancel() @@ -300,7 +301,7 @@ func (n *NodeClient) NetworkListInterfaces(ctx context.Context) (map[string][]ne } // DeploymentChanges return changes of a deployment via contract ID -func (n *NodeClient) DeploymentChanges(ctx context.Context, contractID uint64) (changes []gridtypes.Workload, err error) { +func (n *NodeClient) DeploymentChanges(ctx context.Context, contractID uint64) (changes []zosTypes.Workload, err error) { ctx, cancel := context.WithTimeout(ctx, n.timeout) defer cancel() diff --git a/grid-client/scripts/deploy_single_vm_with_mycelium/main.go b/grid-client/scripts/deploy_single_vm_with_mycelium/main.go index 94923e355..213732c4b 100644 --- a/grid-client/scripts/deploy_single_vm_with_mycelium/main.go +++ b/grid-client/scripts/deploy_single_vm_with_mycelium/main.go @@ -10,8 +10,8 @@ import ( "github.com/rs/zerolog/log" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/deployer" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "github.com/threefoldtech/tfgrid-sdk-go/grid-proxy/pkg/types" - "github.com/threefoldtech/zos/pkg/gridtypes" ) func main() { @@ -35,10 +35,10 @@ func main() { Name: "test_net", Description: "network to deploy vm with mycelium", Nodes: []uint32{nodeID}, - IPRange: gridtypes.NewIPNet(net.IPNet{ + IPRange: zos.IPNet{IPNet: net.IPNet{ IP: net.IPv4(10, 1, 0, 0), Mask: net.CIDRMask(16, 32), - }), + }}, AddWGAccess: false, MyceliumKeys: map[uint32][]byte{nodeID: myceliumKey}, } @@ -69,7 +69,7 @@ func main() { }, } - dl := workloads.NewDeployment("vm_with_mycelium", nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm}, nil, nil) + dl := workloads.NewDeployment("vm_with_mycelium", nodeID, "", nil, network.Name, nil, nil, []workloads.VM{vm}, nil, nil, nil) err = tf.DeploymentDeployer.Deploy(context.Background(), &dl) if err != nil { log.Fatal().Err(err).Send() diff --git a/grid-client/scripts/deploy_single_vm_with_mycelium_light/main.go b/grid-client/scripts/deploy_single_vm_with_mycelium_light/main.go new file mode 100644 index 000000000..968c80dd4 --- /dev/null +++ b/grid-client/scripts/deploy_single_vm_with_mycelium_light/main.go @@ -0,0 +1,124 @@ +package main + +import ( + "context" + "errors" + "flag" + "net" + "os" + + "github.com/rs/zerolog/log" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/deployer" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" + "github.com/threefoldtech/tfgrid-sdk-go/grid-proxy/pkg/types" +) + +func main() { + ctx := context.Background() + tf, publicKey, err := setup() + if err != nil { + log.Fatal().Err(err).Send() + } + + nodeID, err := filterNode(tf) + if err != nil { + log.Fatal().Err(err).Msg("no available nodes found") + } + + myceliumKey, err := workloads.RandomMyceliumKey() + if err != nil { + log.Debug().Err(err).Send() + } + + network := workloads.ZNetLight{ + Name: "test_net_light", + Description: "network to deploy vm with mycelium", + Nodes: []uint32{nodeID}, + IPRange: zos.IPNet{IPNet: net.IPNet{ + IP: net.IPv4(10, 1, 0, 0), + Mask: net.CIDRMask(16, 32), + }}, + MyceliumKeys: map[uint32][]byte{nodeID: myceliumKey}, + } + + err = tf.NetworkDeployer.Deploy(ctx, &network) + if err != nil { + log.Fatal().Err(err).Send() + } + + myceliumSeed, err := workloads.RandomMyceliumIPSeed() + if err != nil { + log.Debug().Err(err).Send() + } + + vm := workloads.VMLight{ + Name: "vm", + NodeID: nodeID, + NetworkName: network.Name, + CPU: 1, + MemoryMB: 256, + RootfsSizeMB: 10 * 1024, + Flist: "https://hub.grid.tf/tf-official-apps/base:latest.flist", + Entrypoint: "/sbin/zinit init", + IP: "10.1.2.5", + MyceliumIPSeed: myceliumSeed, + EnvVars: map[string]string{ + "SSH_KEY": publicKey, + }, + } + + dl := workloads.NewDeployment("vm_with_mycelium", nodeID, "", nil, network.Name, nil, nil, nil, []workloads.VMLight{vm}, nil, nil) + err = tf.DeploymentDeployer.Deploy(context.Background(), &dl) + if err != nil { + log.Fatal().Err(err).Send() + } + + dl, err = tf.State.LoadDeploymentFromGrid(ctx, nodeID, dl.Name) + if err != nil { + log.Fatal().Err(err).Send() + } + + log.Info().Str("mycelium ip", dl.VmsLight[0].MyceliumIP).Send() +} + +func setup() (deployer.TFPluginClient, string, error) { + mnemonic := os.Getenv("MNEMONIC") + log.Debug().Str("MNEMONIC", mnemonic).Send() + + n := os.Getenv("NETWORK") + log.Debug().Str("NETWORK", n).Send() + + var publicKeyPath string + flag.StringVar(&publicKeyPath, "ssh-key", "", "path to user ssh key") + flag.Parse() + if publicKeyPath == "" { + return deployer.TFPluginClient{}, "", errors.New("path to ssh key should not be empty") + } + + publicKey, err := os.ReadFile(publicKeyPath) + if err != nil { + return deployer.TFPluginClient{}, "", err + } + + tf, err := deployer.NewTFPluginClient(mnemonic, deployer.WithNetwork(n)) + if err != nil { + return deployer.TFPluginClient{}, "", err + } + return tf, string(publicKey), nil +} + +func filterNode(tf deployer.TFPluginClient) (uint32, error) { + f := types.NodeFilter{Status: []string{"up"}, Features: []string{zos.NetworkLightType, zos.ZMachineLightType}} + nodes, err := deployer.FilterNodes(context.Background(), tf, f, nil, nil, []uint64{convertGBToBytes(10)}, 1) + if err != nil { + return 0, err + } + + return uint32(nodes[0].NodeID), err +} + +func convertGBToBytes(gb uint64) uint64 { + bytes := gb * 1024 * 1024 * 1024 + return bytes +} diff --git a/grid-client/state/network_state.go b/grid-client/state/network_state.go index 66166207d..1731f87df 100644 --- a/grid-client/state/network_state.go +++ b/grid-client/state/network_state.go @@ -4,7 +4,7 @@ package state import ( "sync" - "github.com/threefoldtech/zos/pkg/gridtypes" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) // NetworkState is a struct of networks names and their networks and mutex to protect the state @@ -38,7 +38,7 @@ func (nm *NetworkState) GetNetwork(networkName string) Network { } // UpdateNetworkSubnets updates a network subnets given its name -func (nm *NetworkState) UpdateNetworkSubnets(networkName string, ipRange map[uint32]gridtypes.IPNet) { +func (nm *NetworkState) UpdateNetworkSubnets(networkName string, ipRange map[uint32]zos.IPNet) { network := nm.GetNetwork(networkName) network.Subnets = map[uint32]string{} for nodeID, subnet := range ipRange { diff --git a/grid-client/state/network_state_test.go b/grid-client/state/network_state_test.go index 963dd8914..89b597fee 100644 --- a/grid-client/state/network_state_test.go +++ b/grid-client/state/network_state_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" - "github.com/threefoldtech/zos/pkg/gridtypes" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) var nodeID uint32 = 10 @@ -17,10 +17,10 @@ func constructTestNetwork() workloads.ZNet { Name: "network", Description: "network for testing", Nodes: []uint32{nodeID}, - IPRange: gridtypes.NewIPNet(net.IPNet{ + IPRange: zos.IPNet{IPNet: net.IPNet{ IP: net.IPv4(10, 1, 0, 0), Mask: net.CIDRMask(16, 32), - }), + }}, AddWGAccess: false, } } diff --git a/grid-client/state/state.go b/grid-client/state/state.go index a007bfabd..5e5fdbbc1 100644 --- a/grid-client/state/state.go +++ b/grid-client/state/state.go @@ -13,6 +13,7 @@ import ( client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/subi" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "github.com/threefoldtech/zos/pkg/gridtypes" "github.com/threefoldtech/zos/pkg/gridtypes/zos" "golang.org/x/exp/maps" @@ -90,7 +91,7 @@ func (st *State) LoadGatewayFQDNFromGrid(ctx context.Context, nodeID uint32, nam if err != nil { return workloads.GatewayFQDNProxy{}, errors.Wrapf(err, "could not generate deployment metadata for %s", name) } - gateway, err := workloads.NewGatewayFQDNProxyFromZosWorkload(wl) + gateway, err := workloads.NewGatewayFQDNProxyFromZosWorkload(*wl.Workload3()) if err != nil { return workloads.GatewayFQDNProxy{}, err } @@ -126,7 +127,7 @@ func (st *State) LoadGatewayNameFromGrid(ctx context.Context, nodeID uint32, nam if err != nil { return workloads.GatewayNameProxy{}, errors.Wrapf(err, "could not generate deployment metadata for %s", deploymentName) } - gateway, err := workloads.NewGatewayNameProxyFromZosWorkload(wl) + gateway, err := workloads.NewGatewayNameProxyFromZosWorkload(*wl.Workload3()) if err != nil { return workloads.GatewayNameProxy{}, err } @@ -158,9 +159,19 @@ func (st *State) LoadVMFromGrid(ctx context.Context, nodeID uint32, name string, return workloads.NewVMFromWorkload(&wl, &dl, nodeID) } +// LoadVMLightFromGrid loads a vm-light from a grid +func (st *State) LoadVMLightFromGrid(ctx context.Context, nodeID uint32, name string, deploymentName string) (workloads.VMLight, error) { + wl, dl, err := st.GetWorkloadInDeployment(ctx, nodeID, name, deploymentName) + if err != nil { + return workloads.VMLight{}, errors.Wrapf(err, "could not get workload from node %d", nodeID) + } + + return workloads.NewVMLightFromWorkload(&wl, &dl, nodeID) +} + // LoadK8sFromGrid loads k8s from grid func (st *State) LoadK8sFromGrid(ctx context.Context, nodeIDs []uint32, deploymentName string) (workloads.K8sCluster, error) { - clusterDeployments := make(map[uint32]gridtypes.Deployment) + clusterDeployments := make(map[uint32]zosTypes.Deployment) nodeDeploymentID := map[uint32]uint64{} for _, nodeID := range nodeIDs { _, deployment, err := st.GetWorkloadInDeployment(ctx, nodeID, "", deploymentName) @@ -175,7 +186,7 @@ func (st *State) LoadK8sFromGrid(ctx context.Context, nodeIDs []uint32, deployme for nodeID, deployment := range clusterDeployments { for _, workload := range deployment.Workloads { - if workload.Type != zos.ZMachineType { + if workload.Type != zos.ZMachineType.String() { continue } workloadDiskSize, workloadComputedIP, workloadComputedIP6, err := st.computeK8sDeploymentResources(deployment) @@ -183,12 +194,12 @@ func (st *State) LoadK8sFromGrid(ctx context.Context, nodeIDs []uint32, deployme return workloads.K8sCluster{}, errors.Wrapf(err, "could not compute node %s, resources", workload.Name) } - node, err := workloads.NewK8sNodeFromWorkload(workload, nodeID, workloadDiskSize[workload.Name.String()], workloadComputedIP[workload.Name.String()], workloadComputedIP6[workload.Name.String()]) + node, err := workloads.NewK8sNodeFromWorkload(*workload.Workload3(), nodeID, workloadDiskSize[workload.Name], workloadComputedIP[workload.Name], workloadComputedIP6[workload.Name]) if err != nil { return workloads.K8sCluster{}, errors.Wrapf(err, "could not generate node data for %s", workload.Name) } - isMaster, err := isMasterNode(workload) + isMaster, err := isMasterNode(*workload.Workload3()) if err != nil { return workloads.K8sCluster{}, err } @@ -244,7 +255,7 @@ func isMasterNode(workload gridtypes.Workload) (bool, error) { return false, nil } -func (st *State) computeK8sDeploymentResources(dl gridtypes.Deployment) ( +func (st *State) computeK8sDeploymentResources(dl zosTypes.Deployment) ( workloadDiskSize map[string]uint64, workloadComputedIP map[string]string, workloadComputedIP6 map[string]string, @@ -260,32 +271,32 @@ func (st *State) computeK8sDeploymentResources(dl gridtypes.Deployment) ( for _, w := range dl.Workloads { switch w.Type { - case zos.PublicIPType: + case zos.PublicIPType.String(): d := zos.PublicIPResult{} if err := json.Unmarshal(w.Result.Data, &d); err != nil { return workloadDiskSize, workloadComputedIP, workloadComputedIP6, errors.Wrap(err, "failed to load public ip data") } - publicIPs[string(w.Name)] = d.IP.String() - publicIP6s[string(w.Name)] = d.IPv6.String() + publicIPs[w.Name] = d.IP.String() + publicIP6s[w.Name] = d.IPv6.String() - case zos.ZMountType: + case zos.ZMountType.String(): - d, err := w.WorkloadData() + d, err := w.Workload3().WorkloadData() if err != nil { return workloadDiskSize, workloadComputedIP, workloadComputedIP6, errors.Wrap(err, "failed to load disk data") } - diskSize[string(w.Name)] = uint64(d.(*zos.ZMount).Size / gridtypes.Gigabyte) + diskSize[w.Name] = uint64(d.(*zos.ZMount).Size / gridtypes.Gigabyte) } } for _, w := range dl.Workloads { - if w.Type == zos.ZMachineType { + if w.Type == zos.ZMachineType.String() { publicIPKey := fmt.Sprintf("%sip", w.Name) diskKey := fmt.Sprintf("%sdisk", w.Name) - workloadDiskSize[string(w.Name)] = diskSize[diskKey] - workloadComputedIP[string(w.Name)] = publicIPs[publicIPKey] - workloadComputedIP6[string(w.Name)] = publicIP6s[publicIPKey] + workloadDiskSize[w.Name] = diskSize[diskKey] + workloadComputedIP[w.Name] = publicIPs[publicIPKey] + workloadComputedIP6[w.Name] = publicIP6s[publicIPKey] } } @@ -328,7 +339,7 @@ func (st *State) LoadNetworkFromGrid(ctx context.Context, name string) (znet wor } for _, wl := range dl.Workloads { - if wl.Type == zos.NetworkType && wl.Name == gridtypes.Name(name) { + if wl.Type == zosTypes.NetworkType && wl.Name == name { znet, err = workloads.NewNetworkFromWorkload(wl, nodeID) if err != nil { return workloads.ZNet{}, errors.Wrapf(err, "failed to get network from workload %s", name) @@ -359,7 +370,7 @@ func (st *State) LoadNetworkFromGrid(ctx context.Context, name string) (znet wor // merge networks var nodes []uint32 - nodesIPRange := map[uint32]gridtypes.IPNet{} + nodesIPRange := map[uint32]zosTypes.IPNet{} wgPort := map[uint32]int{} keys := map[uint32]wgtypes.Key{} for _, net := range zNets { @@ -389,6 +400,76 @@ func (st *State) LoadNetworkFromGrid(ctx context.Context, name string) (znet wor return znet, nil } +// LoadNetworkLightFromGrid loads a network-light from grid +func (st *State) LoadNetworkLightFromGrid(ctx context.Context, name string) (znet workloads.ZNetLight, err error) { + var zNets []workloads.ZNetLight + nodeDeploymentsIDs := map[uint32]uint64{} + + sub := st.Substrate + for nodeID := range st.CurrentNodeDeployments { + nodeClient, err := st.NcPool.GetNodeClient(sub, nodeID) + if err != nil { + return znet, errors.Wrapf(err, "could not get node client: %d", nodeID) + } + + for _, contractID := range st.CurrentNodeDeployments[nodeID] { + dl, err := nodeClient.DeploymentGet(ctx, contractID) + if err != nil { + return znet, errors.Wrapf(err, "could not get network deployment %d from node %d", contractID, nodeID) + } + + if len(strings.TrimSpace(dl.Metadata)) == 0 { + contract, err := sub.GetContract(contractID) + if err != nil { + return znet, errors.Wrapf(err, "could not get contract %d from node %d", contractID, nodeID) + } + dl.Metadata = contract.ContractType.NodeContract.DeploymentData + if len(strings.TrimSpace(dl.Metadata)) == 0 { + return znet, errors.Wrapf(err, "contract %d doesn't have metadata", contractID) + } + } + + deploymentData, err := workloads.ParseDeploymentData(dl.Metadata) + if err != nil { + return znet, errors.Wrapf(err, "could not generate deployment metadata for %s", name) + } + + for _, wl := range dl.Workloads { + if wl.Type == zosTypes.NetworkLightType && wl.Name == name { + znet, err = workloads.NewNetworkLightFromWorkload(wl, nodeID) + if err != nil { + return workloads.ZNetLight{}, errors.Wrapf(err, "failed to get network from workload %s", name) + } + + znet.SolutionType = deploymentData.ProjectName + zNets = append(zNets, znet) + nodeDeploymentsIDs[nodeID] = dl.ContractID + break + } + } + } + } + + if reflect.DeepEqual(znet, workloads.ZNetLight{}) { + return znet, errors.Wrapf(ErrNotFound, "failed to get network %s", name) + } + + // merge networks + var nodes []uint32 + nodesIPRange := map[uint32]zosTypes.IPNet{} + for _, net := range zNets { + maps.Copy(nodesIPRange, net.NodesIPRange) + nodes = append(nodes, net.Nodes...) + } + + znet.NodeDeploymentID = nodeDeploymentsIDs + znet.Nodes = nodes + znet.NodesIPRange = nodesIPRange + + st.Networks.UpdateNetworkSubnets(znet.Name, znet.NodesIPRange) + return znet, nil +} + // LoadDeploymentFromGrid loads deployment from grid func (st *State) LoadDeploymentFromGrid(ctx context.Context, nodeID uint32, name string) (workloads.Deployment, error) { _, deployment, err := st.GetWorkloadInDeployment(ctx, nodeID, "", name) @@ -405,7 +486,10 @@ func (st *State) LoadDeploymentFromGrid(ctx context.Context, nodeID uint32, name _, err = st.LoadNetworkFromGrid(ctx, d.NetworkName) if err != nil { - return workloads.Deployment{}, errors.Wrapf(err, "failed to load network %s", d.NetworkName) + _, err = st.LoadNetworkLightFromGrid(ctx, d.NetworkName) + if err != nil { + return workloads.Deployment{}, errors.Wrapf(err, "failed to load network %s", d.NetworkName) + } } d.IPrange = st.Networks.GetNetwork(d.NetworkName).Subnets[nodeID] @@ -414,34 +498,34 @@ func (st *State) LoadDeploymentFromGrid(ctx context.Context, nodeID uint32, name // GetWorkloadInDeployment return a workload in a deployment using their names and node ID // if name is empty it returns a deployment with name equal to deploymentName and empty workload -func (st *State) GetWorkloadInDeployment(ctx context.Context, nodeID uint32, name string, deploymentName string) (gridtypes.Workload, gridtypes.Deployment, error) { +func (st *State) GetWorkloadInDeployment(ctx context.Context, nodeID uint32, name string, deploymentName string) (zosTypes.Workload, zosTypes.Deployment, error) { sub := st.Substrate if contractIDs, ok := st.CurrentNodeDeployments[nodeID]; ok { nodeClient, err := st.NcPool.GetNodeClient(sub, nodeID) if err != nil { - return gridtypes.Workload{}, gridtypes.Deployment{}, errors.Wrapf(err, "could not get node client: %d", nodeID) + return zosTypes.Workload{}, zosTypes.Deployment{}, errors.Wrapf(err, "could not get node client: %d", nodeID) } for _, contractID := range contractIDs { dl, err := nodeClient.DeploymentGet(ctx, contractID) if err != nil { - return gridtypes.Workload{}, gridtypes.Deployment{}, errors.Wrapf(err, "could not get deployment %d from node %d", contractID, nodeID) + return zosTypes.Workload{}, zosTypes.Deployment{}, errors.Wrapf(err, "could not get deployment %d from node %d", contractID, nodeID) } if len(strings.TrimSpace(dl.Metadata)) == 0 { contract, err := sub.GetContract(contractID) if err != nil { - return gridtypes.Workload{}, gridtypes.Deployment{}, errors.Wrapf(err, "could not get contract %d from node %d", contractID, nodeID) + return zosTypes.Workload{}, zosTypes.Deployment{}, errors.Wrapf(err, "could not get contract %d from node %d", contractID, nodeID) } dl.Metadata = contract.ContractType.NodeContract.DeploymentData if len(strings.TrimSpace(dl.Metadata)) == 0 { - return gridtypes.Workload{}, gridtypes.Deployment{}, errors.Wrapf(err, "contract %d doesn't have metadata", contractID) + return zosTypes.Workload{}, zosTypes.Deployment{}, errors.Wrapf(err, "contract %d doesn't have metadata", contractID) } } dlData, err := workloads.ParseDeploymentData(dl.Metadata) if err != nil { - return gridtypes.Workload{}, gridtypes.Deployment{}, errors.Wrapf(err, "could not get deployment %d data", contractID) + return zosTypes.Workload{}, zosTypes.Deployment{}, errors.Wrapf(err, "could not get deployment %d data", contractID) } if dlData.Name != deploymentName { @@ -449,18 +533,18 @@ func (st *State) GetWorkloadInDeployment(ctx context.Context, nodeID uint32, nam } if name == "" { - return gridtypes.Workload{}, dl, nil + return zosTypes.Workload{}, dl, nil } for _, workload := range dl.Workloads { - if workload.Name == gridtypes.Name(name) { + if workload.Name == name { return workload, dl, nil } } } - return gridtypes.Workload{}, gridtypes.Deployment{}, errors.Wrapf(ErrNotFound, "failed to find workload '%s'", name) + return zosTypes.Workload{}, zosTypes.Deployment{}, errors.Wrapf(ErrNotFound, "failed to find workload '%s'", name) } - return gridtypes.Workload{}, gridtypes.Deployment{}, errors.Wrapf(ErrNotFound, "failed to find deployment %s on node %d", name, nodeID) + return zosTypes.Workload{}, zosTypes.Deployment{}, errors.Wrapf(ErrNotFound, "failed to find deployment %s on node %d", name, nodeID) } // AssignNodesIPRange to assign ip range of k8s cluster nodes diff --git a/grid-client/state/state_test.go b/grid-client/state/state_test.go index 8503e4aa8..af7539db0 100644 --- a/grid-client/state/state_test.go +++ b/grid-client/state/state_test.go @@ -1,4 +1,4 @@ -// Package state for grid state +// // Package state for grid state package state import ( @@ -13,6 +13,7 @@ import ( "github.com/threefoldtech/tfgrid-sdk-go/grid-client/mocks" client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "github.com/threefoldtech/zos/pkg/gridtypes" "github.com/threefoldtech/zos/pkg/gridtypes/zos" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" @@ -23,7 +24,7 @@ const ( invalid = "invalid" ) -func SetupLoaderTests(t *testing.T, wls []gridtypes.Workload) *State { +func SetupLoaderTests(t *testing.T, wls []zosTypes.Workload) *State { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -34,7 +35,7 @@ func SetupLoaderTests(t *testing.T, wls []gridtypes.Workload) *State { state := NewState(ncPool, sub) state.CurrentNodeDeployments = map[uint32]ContractIDs{1: []uint64{10}} - dl1 := workloads.NewGridDeployment(13, wls) + dl1 := workloads.NewGridDeployment(13, 0, wls) dl1.ContractID = 10 ncPool.EXPECT(). @@ -44,7 +45,7 @@ func SetupLoaderTests(t *testing.T, wls []gridtypes.Workload) *State { cl.EXPECT(). Call(gomock.Any(), uint32(13), "zos.deployment.get", gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, twin uint32, fn string, data, result interface{}) error { - var res *gridtypes.Deployment = result.(*gridtypes.Deployment) + var res *zosTypes.Deployment = result.(*zosTypes.Deployment) dl1.Metadata = "{\"type\":\"\",\"name\":\"testName\",\"projectName\":\"\"}" *res = dl1 return nil @@ -64,18 +65,18 @@ func TestLoadDiskFromGrid(t *testing.T) { Description: "test des", } - diskWl := gridtypes.Workload{ - Name: gridtypes.Name("test"), + diskWl := zosTypes.Workload{ + Name: "test", Version: 0, - Type: zos.ZMountType, + Type: zosTypes.ZMountType, Description: "test des", - Data: gridtypes.MustMarshal(zos.ZMount{ - Size: 100 * gridtypes.Gigabyte, + Data: zosTypes.MustMarshal(zosTypes.ZMount{ + Size: 100 * zosTypes.Gigabyte, }), } t.Run("success", func(t *testing.T) { - state := SetupLoaderTests(t, []gridtypes.Workload{diskWl}) + state := SetupLoaderTests(t, []zosTypes.Workload{diskWl}) got, err := state.LoadDiskFromGrid(context.Background(), 1, "test", deploymentName) assert.NoError(t, err) @@ -86,7 +87,7 @@ func TestLoadDiskFromGrid(t *testing.T) { diskWlCp := diskWl diskWlCp.Type = invalid - state := SetupLoaderTests(t, []gridtypes.Workload{diskWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{diskWlCp}) _, err := state.LoadDiskFromGrid(context.Background(), 1, "test", deploymentName) assert.Error(t, err) @@ -94,12 +95,12 @@ func TestLoadDiskFromGrid(t *testing.T) { t.Run("wrong workload data", func(t *testing.T) { diskWlCp := diskWl - diskWlCp.Type = zos.GatewayNameProxyType - diskWlCp.Data = gridtypes.MustMarshal(zos.GatewayNameProxy{ + diskWlCp.Type = zosTypes.GatewayNameProxyType + diskWlCp.Data = zosTypes.MustMarshal(zos.GatewayNameProxy{ Name: "name", }) - state := SetupLoaderTests(t, []gridtypes.Workload{diskWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{diskWlCp}) _, err := state.LoadDiskFromGrid(context.Background(), 1, "test", deploymentName) assert.Error(t, err) @@ -107,11 +108,11 @@ func TestLoadDiskFromGrid(t *testing.T) { } func TestLoadGatewayFQDNFromGrid(t *testing.T) { - gatewayWl := gridtypes.Workload{ + gatewayWl := zosTypes.Workload{ Version: 0, - Type: zos.GatewayFQDNProxyType, - Name: gridtypes.Name("test"), - Data: gridtypes.MustMarshal(zos.GatewayFQDNProxy{ + Type: zosTypes.GatewayFQDNProxyType, + Name: "test", + Data: zosTypes.MustMarshal(zos.GatewayFQDNProxy{ GatewayBase: zos.GatewayBase{ TLSPassthrough: true, Backends: []zos.Backend{"http://1.1.1.1"}, @@ -130,7 +131,7 @@ func TestLoadGatewayFQDNFromGrid(t *testing.T) { } t.Run("success", func(t *testing.T) { - state := SetupLoaderTests(t, []gridtypes.Workload{gatewayWl}) + state := SetupLoaderTests(t, []zosTypes.Workload{gatewayWl}) got, err := state.LoadGatewayFQDNFromGrid(context.Background(), 1, "test", deploymentName) assert.NoError(t, err) @@ -141,7 +142,7 @@ func TestLoadGatewayFQDNFromGrid(t *testing.T) { gatewayWlCp := gatewayWl gatewayWlCp.Type = invalid - state := SetupLoaderTests(t, []gridtypes.Workload{gatewayWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{gatewayWlCp}) _, err := state.LoadGatewayFQDNFromGrid(context.Background(), 1, "test", deploymentName) assert.Error(t, err) @@ -149,12 +150,12 @@ func TestLoadGatewayFQDNFromGrid(t *testing.T) { t.Run("wrong workload data", func(t *testing.T) { gatewayWlCp := gatewayWl - gatewayWlCp.Type = zos.GatewayNameProxyType - gatewayWlCp.Data = gridtypes.MustMarshal(zos.GatewayNameProxy{ + gatewayWlCp.Type = zosTypes.GatewayNameProxyType + gatewayWlCp.Data = zosTypes.MustMarshal(zos.GatewayNameProxy{ Name: "name", }) - state := SetupLoaderTests(t, []gridtypes.Workload{gatewayWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{gatewayWlCp}) _, err := state.LoadGatewayFQDNFromGrid(context.Background(), 1, "test", deploymentName) assert.Error(t, err) @@ -167,20 +168,20 @@ func TestLoadGatewayNameFromGrid(t *testing.T) { }) assert.NoError(t, err) - gatewayWl := gridtypes.Workload{ + gatewayWl := zosTypes.Workload{ Version: 0, - Type: zos.GatewayNameProxyType, - Name: gridtypes.Name(deploymentName), - Data: gridtypes.MustMarshal(zos.GatewayNameProxy{ + Type: zosTypes.GatewayNameProxyType, + Name: deploymentName, + Data: zosTypes.MustMarshal(zos.GatewayNameProxy{ GatewayBase: zos.GatewayBase{ TLSPassthrough: true, Backends: []zos.Backend{"http://1.1.1.1"}, }, Name: "test", }), - Result: gridtypes.Result{ + Result: zosTypes.Result{ Created: 1000, - State: gridtypes.StateOk, + State: zosTypes.StateOk, Data: res, }, } @@ -195,7 +196,7 @@ func TestLoadGatewayNameFromGrid(t *testing.T) { } t.Run("success", func(t *testing.T) { - state := SetupLoaderTests(t, []gridtypes.Workload{gatewayWl}) + state := SetupLoaderTests(t, []zosTypes.Workload{gatewayWl}) got, err := state.LoadGatewayNameFromGrid(context.Background(), 1, "test", deploymentName) assert.NoError(t, err) @@ -205,7 +206,7 @@ func TestLoadGatewayNameFromGrid(t *testing.T) { gatewayWlCp := gatewayWl gatewayWlCp.Type = invalid - state := SetupLoaderTests(t, []gridtypes.Workload{gatewayWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{gatewayWlCp}) _, err := state.LoadGatewayNameFromGrid(context.Background(), 1, "test", deploymentName) assert.Error(t, err) @@ -213,12 +214,12 @@ func TestLoadGatewayNameFromGrid(t *testing.T) { t.Run("wrong workload data", func(t *testing.T) { gatewayWlCp := gatewayWl - gatewayWlCp.Type = zos.GatewayFQDNProxyType - gatewayWlCp.Data = gridtypes.MustMarshal(zos.GatewayFQDNProxy{ + gatewayWlCp.Type = zosTypes.GatewayFQDNProxyType + gatewayWlCp.Data = zosTypes.MustMarshal(zos.GatewayFQDNProxy{ FQDN: "123", }) - state := SetupLoaderTests(t, []gridtypes.Workload{gatewayWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{gatewayWlCp}) _, err := state.LoadGatewayNameFromGrid(context.Background(), 1, "test", deploymentName) assert.Error(t, err) @@ -253,7 +254,7 @@ func TestLoadK8sFromGrid(t *testing.T) { var Workers []workloads.K8sNode - ipRange, err := gridtypes.ParseIPNet("1.1.1.1/24") + ipRange, err := zosTypes.ParseIPNet("1.1.1.1/24") assert.NoError(t, err) cluster := workloads.K8sCluster{ @@ -266,44 +267,44 @@ func TestLoadK8sFromGrid(t *testing.T) { FlistChecksum: flistCheckSum, NodeDeploymentID: map[uint32]uint64{1: 10}, NodesIPRange: map[uint32]gridtypes.IPNet{ - 1: ipRange, + 1: gridtypes.IPNet(ipRange), }, } - k8sWorkload := gridtypes.Workload{ + k8sWorkload := zosTypes.Workload{ Version: 0, - Name: gridtypes.Name("test"), - Type: zos.ZMachineType, - Data: gridtypes.MustMarshal(zos.ZMachine{ + Name: "test", + Type: zosTypes.ZMachineType, + Data: zosTypes.MustMarshal(zosTypes.ZMachine{ FList: flist, - Network: zos.MachineNetwork{ - Interfaces: []zos.MachineInterface{ + Network: zosTypes.MachineNetwork{ + Interfaces: []zosTypes.MachineInterface{ { - Network: gridtypes.Name("test"), + Network: "test", IP: net.ParseIP("1.1.1.1"), }, }, Planetary: true, }, Size: 100, - ComputeCapacity: zos.MachineCapacity{ + ComputeCapacity: zosTypes.MachineCapacity{ CPU: 1, - Memory: 8 * gridtypes.Megabyte, + Memory: 8 * zosTypes.Megabyte, }, - Mounts: []zos.MachineMount{}, + Mounts: []zosTypes.MachineMount{}, Entrypoint: "", Env: map[string]string{}, Corex: false, }), - Result: gridtypes.Result{ + Result: zosTypes.Result{ Created: 5000, - State: gridtypes.StateOk, + State: zosTypes.StateOk, Data: res, }, } metadata, err := json.Marshal(workloads.NetworkMetaData{ - Version: workloads.Version, + Version: int(workloads.Version3), UserAccesses: []workloads.UserAccess{ { Subnet: "", @@ -314,24 +315,24 @@ func TestLoadK8sFromGrid(t *testing.T) { }) assert.NoError(t, err) - networkWl := gridtypes.Workload{ + networkWl := zosTypes.Workload{ Version: 0, - Name: gridtypes.Name("test"), - Type: zos.NetworkType, - Data: gridtypes.MustMarshal(zos.Network{ - NetworkIPRange: gridtypes.MustParseIPNet(ipRange.String()), + Name: "test", + Type: zosTypes.NetworkType, + Data: zosTypes.MustMarshal(zosTypes.Network{ + NetworkIPRange: zosTypes.MustParseIPNet(ipRange.String()), Subnet: ipRange, WGPrivateKey: "", WGListenPort: 0, - Peers: []zos.Peer{}, + Peers: []zosTypes.Peer{}, }), Metadata: string(metadata), Description: "test description", - Result: gridtypes.Result{}, + Result: zosTypes.Result{}, } t.Run("success", func(t *testing.T) { - state := SetupLoaderTests(t, []gridtypes.Workload{networkWl, k8sWorkload}) + state := SetupLoaderTests(t, []zosTypes.Workload{networkWl, k8sWorkload}) got, err := state.LoadK8sFromGrid(context.Background(), []uint32{1}, deploymentName) assert.NoError(t, err) @@ -342,7 +343,7 @@ func TestLoadK8sFromGrid(t *testing.T) { k8sWorkloadCp := k8sWorkload k8sWorkloadCp.Type = invalid - state := SetupLoaderTests(t, []gridtypes.Workload{k8sWorkloadCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{k8sWorkloadCp}) _, err := state.LoadK8sFromGrid(context.Background(), []uint32{1}, deploymentName) assert.Error(t, err) @@ -350,12 +351,12 @@ func TestLoadK8sFromGrid(t *testing.T) { t.Run("wrong workload data", func(t *testing.T) { k8sWorkloadCp := k8sWorkload - k8sWorkloadCp.Type = zos.ZMachineType - k8sWorkloadCp.Data = gridtypes.MustMarshal(zos.ZMachine{ + k8sWorkloadCp.Type = zosTypes.ZMachineType + k8sWorkloadCp.Data = zosTypes.MustMarshal(zosTypes.ZMachine{ FList: "", }) - state := SetupLoaderTests(t, []gridtypes.Workload{k8sWorkloadCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{k8sWorkloadCp}) _, err := state.LoadK8sFromGrid(context.Background(), []uint32{1}, deploymentName) assert.Error(t, err) @@ -363,7 +364,7 @@ func TestLoadK8sFromGrid(t *testing.T) { } func TestLoadNetworkFromGrid(t *testing.T) { - ipRange, err := gridtypes.ParseIPNet("1.1.1.1/24") + ipRange, err := zosTypes.ParseIPNet("1.1.1.1/24") assert.NoError(t, err) znet := workloads.ZNet{ @@ -375,12 +376,12 @@ func TestLoadNetworkFromGrid(t *testing.T) { NodeDeploymentID: map[uint32]uint64{1: 10}, WGPort: map[uint32]int{}, Keys: map[uint32]wgtypes.Key{}, - NodesIPRange: map[uint32]gridtypes.IPNet{1: ipRange}, + NodesIPRange: map[uint32]zosTypes.IPNet{1: ipRange}, MyceliumKeys: make(map[uint32][]byte), } metadata, err := json.Marshal(workloads.NetworkMetaData{ - Version: workloads.Version, + Version: int(workloads.Version3), UserAccesses: []workloads.UserAccess{ { Subnet: "", @@ -391,24 +392,24 @@ func TestLoadNetworkFromGrid(t *testing.T) { }) assert.NoError(t, err) - networkWl := gridtypes.Workload{ + networkWl := zosTypes.Workload{ Version: 0, - Name: gridtypes.Name("test"), - Type: zos.NetworkType, - Data: gridtypes.MustMarshal(zos.Network{ - NetworkIPRange: gridtypes.MustParseIPNet(znet.IPRange.String()), + Name: "test", + Type: zosTypes.NetworkType, + Data: zosTypes.MustMarshal(zosTypes.Network{ + NetworkIPRange: zosTypes.MustParseIPNet(znet.IPRange.String()), Subnet: ipRange, WGPrivateKey: "", WGListenPort: 0, - Peers: []zos.Peer{}, + Peers: []zosTypes.Peer{}, }), Metadata: string(metadata), Description: "test description", - Result: gridtypes.Result{}, + Result: zosTypes.Result{}, } t.Run("success", func(t *testing.T) { - state := SetupLoaderTests(t, []gridtypes.Workload{networkWl}) + state := SetupLoaderTests(t, []zosTypes.Workload{networkWl}) got, err := state.LoadNetworkFromGrid(context.Background(), "test") assert.NoError(t, err) @@ -419,7 +420,7 @@ func TestLoadNetworkFromGrid(t *testing.T) { networkWlCp := networkWl networkWlCp.Type = invalid - state := SetupLoaderTests(t, []gridtypes.Workload{networkWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{networkWlCp}) _, err := state.LoadNetworkFromGrid(context.Background(), "test") assert.Error(t, err) @@ -427,18 +428,80 @@ func TestLoadNetworkFromGrid(t *testing.T) { t.Run("wrong workload data", func(t *testing.T) { networkWlCp := networkWl - networkWlCp.Type = zos.GatewayNameProxyType - networkWlCp.Data = gridtypes.MustMarshal(zos.Network{ + networkWlCp.Type = zosTypes.GatewayNameProxyType + networkWlCp.Data = zosTypes.MustMarshal(zosTypes.Network{ WGPrivateKey: "key", }) - state := SetupLoaderTests(t, []gridtypes.Workload{networkWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{networkWlCp}) _, err := state.LoadNetworkFromGrid(context.Background(), "test") assert.Error(t, err) }) } +func TestLoadNetworkLightFromGrid(t *testing.T) { + ipRange, err := zosTypes.ParseIPNet("1.1.1.1/24") + assert.NoError(t, err) + + znet := workloads.ZNetLight{ + Name: "test", + Description: "test description", + Nodes: []uint32{1}, + NodeDeploymentID: map[uint32]uint64{1: 10}, + NodesIPRange: map[uint32]zosTypes.IPNet{1: ipRange}, + MyceliumKeys: map[uint32][]byte{1: zosTypes.Bytes{}}, + } + + metadata, err := json.Marshal(workloads.NetworkMetaData{ + Version: int(workloads.Version3), + }) + assert.NoError(t, err) + + networkWl := zosTypes.Workload{ + Version: 0, + Name: "test", + Type: zosTypes.NetworkLightType, + Data: zosTypes.MustMarshal(zosTypes.NetworkLight{ + Subnet: ipRange, + }), + Metadata: string(metadata), + Description: "test description", + Result: zosTypes.Result{}, + } + + t.Run("success", func(t *testing.T) { + state := SetupLoaderTests(t, []zosTypes.Workload{networkWl}) + + got, err := state.LoadNetworkLightFromGrid(context.Background(), "test") + assert.NoError(t, err) + assert.Equal(t, znet, got) + }) + + t.Run("invalid type", func(t *testing.T) { + networkWlCp := networkWl + networkWlCp.Type = invalid + + state := SetupLoaderTests(t, []zosTypes.Workload{networkWlCp}) + + _, err := state.LoadNetworkLightFromGrid(context.Background(), "test") + assert.Error(t, err) + }) + + t.Run("wrong workload data", func(t *testing.T) { + networkWlCp := networkWl + networkWlCp.Type = zosTypes.GatewayNameProxyType + networkWlCp.Data = zosTypes.MustMarshal(zosTypes.Network{ + WGPrivateKey: "key", + }) + + state := SetupLoaderTests(t, []zosTypes.Workload{networkWlCp}) + + _, err := state.LoadNetworkLightFromGrid(context.Background(), "test") + assert.Error(t, err) + }) +} + func TestLoadQSFSFromGrid(t *testing.T) { res, err := json.Marshal(zos.QuatumSafeFSResult{ Path: "path", @@ -449,47 +512,47 @@ func TestLoadQSFSFromGrid(t *testing.T) { k, err := hex.DecodeString("4d778ba3216e4da4231540c92a55f06157cabba802f9b68fb0f78375d2e825af") assert.NoError(t, err) - qsfsWl := gridtypes.Workload{ + qsfsWl := zosTypes.Workload{ Version: 0, - Name: gridtypes.Name("test"), - Type: zos.QuantumSafeFSType, + Name: "test", + Type: zosTypes.QuantumSafeFSType, Description: "test des", - Data: gridtypes.MustMarshal(zos.QuantumSafeFS{ - Cache: 2048 * gridtypes.Megabyte, - Config: zos.QuantumSafeFSConfig{ + Data: zosTypes.MustMarshal(zosTypes.QuantumSafeFS{ + Cache: 2048 * zosTypes.Megabyte, + Config: zosTypes.QuantumSafeFSConfig{ MinimalShards: 10, ExpectedShards: 20, RedundantGroups: 2, RedundantNodes: 5, MaxZDBDataDirSize: 10, - Encryption: zos.Encryption{ - Algorithm: zos.EncryptionAlgorithm("AES"), - Key: zos.EncryptionKey(k), + Encryption: zosTypes.Encryption{ + Algorithm: zosTypes.EncryptionAlgorithm("AES"), + Key: zosTypes.EncryptionKey(k), }, - Meta: zos.QuantumSafeMeta{ + Meta: zosTypes.QuantumSafeMeta{ Type: "zdb", - Config: zos.QuantumSafeConfig{ + Config: zosTypes.QuantumSafeConfig{ Prefix: "test", - Encryption: zos.Encryption{ - Algorithm: zos.EncryptionAlgorithm("AES"), - Key: zos.EncryptionKey(k), + Encryption: zosTypes.Encryption{ + Algorithm: zosTypes.EncryptionAlgorithm("AES"), + Key: zosTypes.EncryptionKey(k), }, - Backends: []zos.ZdbBackend{ + Backends: []zosTypes.ZdbBackend{ {Address: "1.1.1.1", Namespace: "test ns", Password: "password"}, }, }, }, - Groups: []zos.ZdbGroup{{Backends: []zos.ZdbBackend{ + Groups: []zosTypes.ZdbGroup{{Backends: []zosTypes.ZdbBackend{ {Address: "2.2.2.2", Namespace: "test ns2", Password: "password2"}, }}}, - Compression: zos.QuantumCompression{ + Compression: zosTypes.QuantumCompression{ Algorithm: "snappy", }, }, }), - Result: gridtypes.Result{ + Result: zosTypes.Result{ Created: 10000, - State: gridtypes.StateOk, + State: zosTypes.StateOk, Data: res, }, } @@ -522,7 +585,7 @@ func TestLoadQSFSFromGrid(t *testing.T) { } t.Run("success", func(t *testing.T) { - state := SetupLoaderTests(t, []gridtypes.Workload{qsfsWl}) + state := SetupLoaderTests(t, []zosTypes.Workload{qsfsWl}) got, err := state.LoadQSFSFromGrid(context.Background(), 1, "test", deploymentName) assert.NoError(t, err) @@ -532,7 +595,7 @@ func TestLoadQSFSFromGrid(t *testing.T) { qsfsWlCp := qsfsWl qsfsWlCp.Type = invalid - state := SetupLoaderTests(t, []gridtypes.Workload{qsfsWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{qsfsWlCp}) _, err := state.LoadQSFSFromGrid(context.Background(), 1, "test", deploymentName) assert.Error(t, err) @@ -540,12 +603,12 @@ func TestLoadQSFSFromGrid(t *testing.T) { t.Run("wrong workload data", func(t *testing.T) { qsfsWlCp := qsfsWl - qsfsWlCp.Type = zos.GatewayNameProxyType - qsfsWlCp.Data = gridtypes.MustMarshal(zos.GatewayNameProxy{ + qsfsWlCp.Type = zosTypes.GatewayNameProxyType + qsfsWlCp.Data = zosTypes.MustMarshal(zos.GatewayNameProxy{ Name: "name", }) - state := SetupLoaderTests(t, []gridtypes.Workload{qsfsWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{qsfsWlCp}) _, err := state.LoadQSFSFromGrid(context.Background(), 1, "test", deploymentName) assert.Error(t, err) @@ -555,7 +618,7 @@ func TestLoadQSFSFromGrid(t *testing.T) { qsfsWlCp := qsfsWl qsfsWlCp.Result.Data = nil - state := SetupLoaderTests(t, []gridtypes.Workload{qsfsWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{qsfsWlCp}) _, err := state.LoadQSFSFromGrid(context.Background(), 1, "test", deploymentName) assert.Error(t, err) @@ -597,53 +660,53 @@ func TestLoadVMFromGrid(t *testing.T) { NetworkName: "test_network", } - pubWl := gridtypes.Workload{ + pubWl := zosTypes.Workload{ Version: 0, - Name: gridtypes.Name("testip"), - Type: zos.PublicIPType, - Data: gridtypes.MustMarshal(zos.PublicIP{ + Name: "testip", + Type: zosTypes.PublicIPType, + Data: zosTypes.MustMarshal(zosTypes.PublicIP{ V4: true, }), } - vmWl := gridtypes.Workload{ + vmWl := zosTypes.Workload{ Version: 0, - Name: gridtypes.Name("test"), - Type: zos.ZMachineType, - Data: gridtypes.MustMarshal(zos.ZMachine{ + Name: "test", + Type: zosTypes.ZMachineType, + Data: zosTypes.MustMarshal(zosTypes.ZMachine{ FList: "https://hub.grid.tf/tf-official-apps/base:latest.flist", - Network: zos.MachineNetwork{ - Interfaces: []zos.MachineInterface{ + Network: zosTypes.MachineNetwork{ + Interfaces: []zosTypes.MachineInterface{ { - Network: gridtypes.Name("test_network"), + Network: "test_network", IP: net.ParseIP("1.1.1.1"), }, }, PublicIP: pubWl.Name, Planetary: true, }, - ComputeCapacity: zos.MachineCapacity{ + ComputeCapacity: zosTypes.MachineCapacity{ CPU: uint8(2), - Memory: 2048 * gridtypes.Megabyte, + Memory: 2048 * zosTypes.Megabyte, }, - Size: 4096 * gridtypes.Megabyte, + Size: 4096 * zosTypes.Megabyte, Entrypoint: "entrypoint", Corex: false, - Mounts: []zos.MachineMount{ - {Name: gridtypes.Name("disk"), Mountpoint: "mount"}, + Mounts: []zosTypes.MachineMount{ + {Name: "disk", Mountpoint: "mount"}, }, Env: map[string]string{"var1": "val1"}, }), Description: "test des", - Result: gridtypes.Result{ + Result: zosTypes.Result{ Created: 5000, - State: gridtypes.StateOk, + State: zosTypes.StateOk, Data: vmRes, }, } t.Run("success", func(t *testing.T) { - state := SetupLoaderTests(t, []gridtypes.Workload{vmWl, pubWl}) + state := SetupLoaderTests(t, []zosTypes.Workload{vmWl, pubWl}) got, err := state.LoadVMFromGrid(context.Background(), vm.NodeID, "test", deploymentName) assert.NoError(t, err) @@ -654,7 +717,7 @@ func TestLoadVMFromGrid(t *testing.T) { vmWlCp := vmWl vmWlCp.Type = invalid - state := SetupLoaderTests(t, []gridtypes.Workload{vmWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{vmWlCp}) _, err := state.LoadVMFromGrid(context.Background(), vm.NodeID, "test", deploymentName) assert.Error(t, err) @@ -662,12 +725,12 @@ func TestLoadVMFromGrid(t *testing.T) { t.Run("wrong workload data", func(t *testing.T) { vmWlCp := vmWl - vmWlCp.Type = zos.GatewayFQDNProxyType - vmWlCp.Data = gridtypes.MustMarshal(zos.GatewayFQDNProxy{ + vmWlCp.Type = zosTypes.GatewayFQDNProxyType + vmWlCp.Data = zosTypes.MustMarshal(zos.GatewayFQDNProxy{ FQDN: "123", }) - state := SetupLoaderTests(t, []gridtypes.Workload{vmWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{vmWlCp}) _, err := state.LoadVMFromGrid(context.Background(), vm.NodeID, "test", deploymentName) assert.Error(t, err) @@ -677,13 +740,119 @@ func TestLoadVMFromGrid(t *testing.T) { vmWlCp := vmWl vmWlCp.Result.Data = nil - state := SetupLoaderTests(t, []gridtypes.Workload{vmWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{vmWlCp}) _, err := state.LoadVMFromGrid(context.Background(), vm.NodeID, "test", deploymentName) assert.Error(t, err) }) } +func TestLoadVMLightFromGrid(t *testing.T) { + vmRes, err := json.Marshal(zos.ZMachineResult{ + ID: "5", + IP: "5.5.5.5", + PlanetaryIP: "203:8b0b:5f3e:b859:c36:efdf:ab6e:50cc", + }) + assert.NoError(t, err) + + var zlogs []workloads.Zlog + + vm := workloads.VMLight{ + Name: "test", + NodeID: 1, + Flist: "https://hub.grid.tf/tf-official-apps/base:latest.flist", + FlistChecksum: "f94b5407f2e8635bd1b6b3dac7fef2d9", + Corex: false, + IP: "1.1.1.1", + Description: "test des", + CPU: 2, + MemoryMB: 2048, + RootfsSizeMB: 4096, + Entrypoint: "entrypoint", + Mounts: []workloads.Mount{ + {Name: "disk", MountPoint: "mount"}, + }, + Zlogs: zlogs, + EnvVars: map[string]string{"var1": "val1"}, + NetworkName: "test_network", + } + + vmWl := zosTypes.Workload{ + Version: 0, + Name: "test", + Type: zosTypes.ZMachineLightType, + Data: zosTypes.MustMarshal(zosTypes.ZMachineLight{ + FList: "https://hub.grid.tf/tf-official-apps/base:latest.flist", + Network: zosTypes.MachineNetworkLight{ + Interfaces: []zosTypes.MachineInterface{ + { + Network: "test_network", + IP: net.ParseIP("1.1.1.1"), + }, + }, + }, + ComputeCapacity: zosTypes.MachineCapacity{ + CPU: uint8(2), + Memory: 2048 * zosTypes.Megabyte, + }, + Size: 4096 * zosTypes.Megabyte, + Entrypoint: "entrypoint", + Corex: false, + Mounts: []zosTypes.MachineMount{ + {Name: "disk", Mountpoint: "mount"}, + }, + Env: map[string]string{"var1": "val1"}, + }), + Description: "test des", + Result: zosTypes.Result{ + Created: 5000, + State: zosTypes.StateOk, + Data: vmRes, + }, + } + + t.Run("success", func(t *testing.T) { + state := SetupLoaderTests(t, []zosTypes.Workload{vmWl}) + + got, err := state.LoadVMLightFromGrid(context.Background(), vm.NodeID, "test", deploymentName) + assert.NoError(t, err) + assert.Equal(t, vm, got) + }) + + t.Run("invalid type", func(t *testing.T) { + vmWlCp := vmWl + vmWlCp.Type = invalid + + state := SetupLoaderTests(t, []zosTypes.Workload{vmWlCp}) + + _, err := state.LoadVMLightFromGrid(context.Background(), vm.NodeID, "test", deploymentName) + assert.Error(t, err) + }) + + t.Run("wrong workload data", func(t *testing.T) { + vmWlCp := vmWl + vmWlCp.Type = zosTypes.GatewayFQDNProxyType + vmWlCp.Data = zosTypes.MustMarshal(zos.GatewayFQDNProxy{ + FQDN: "123", + }) + + state := SetupLoaderTests(t, []zosTypes.Workload{vmWlCp}) + + _, err := state.LoadVMLightFromGrid(context.Background(), vm.NodeID, "test", deploymentName) + assert.Error(t, err) + }) + + t.Run("invalid result data", func(t *testing.T) { + vmWlCp := vmWl + vmWlCp.Result.Data = nil + + state := SetupLoaderTests(t, []zosTypes.Workload{vmWlCp}) + + _, err := state.LoadVMLightFromGrid(context.Background(), vm.NodeID, "test", deploymentName) + assert.Error(t, err) + }) +} + func TestLoadZdbFromGrid(t *testing.T) { res, err := json.Marshal(zos.ZDBResult{ Namespace: "test name", @@ -695,19 +864,19 @@ func TestLoadZdbFromGrid(t *testing.T) { }) assert.NoError(t, err) - zdbWl := gridtypes.Workload{ - Name: gridtypes.Name("test"), - Type: zos.ZDBType, + zdbWl := zosTypes.Workload{ + Name: "test", + Type: zosTypes.ZDBType, Description: "test des", Version: 0, - Result: gridtypes.Result{ + Result: zosTypes.Result{ Created: 1000, - State: gridtypes.StateOk, + State: zosTypes.StateOk, Data: res, }, - Data: gridtypes.MustMarshal(zos.ZDB{ - Size: 100 * gridtypes.Gigabyte, - Mode: zos.ZDBMode("user"), + Data: zosTypes.MustMarshal(zosTypes.ZDB{ + Size: 100 * zosTypes.Gigabyte, + Mode: "user", Password: "password", Public: true, }), @@ -728,7 +897,7 @@ func TestLoadZdbFromGrid(t *testing.T) { } t.Run("success", func(t *testing.T) { - state := SetupLoaderTests(t, []gridtypes.Workload{zdbWl}) + state := SetupLoaderTests(t, []zosTypes.Workload{zdbWl}) got, err := state.LoadZdbFromGrid(context.Background(), 1, "test", deploymentName) assert.NoError(t, err) @@ -739,7 +908,7 @@ func TestLoadZdbFromGrid(t *testing.T) { zdbWlCp := zdbWl zdbWlCp.Type = invalid - state := SetupLoaderTests(t, []gridtypes.Workload{zdbWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{zdbWlCp}) _, err := state.LoadZdbFromGrid(context.Background(), 1, "test", deploymentName) assert.Error(t, err) @@ -747,12 +916,12 @@ func TestLoadZdbFromGrid(t *testing.T) { t.Run("wrong workload data", func(t *testing.T) { zdbWlCp := zdbWl - zdbWlCp.Type = zos.GatewayNameProxyType - zdbWlCp.Data = gridtypes.MustMarshal(zos.GatewayNameProxy{ + zdbWlCp.Type = zosTypes.GatewayNameProxyType + zdbWlCp.Data = zosTypes.MustMarshal(zos.GatewayNameProxy{ Name: "name", }) - state := SetupLoaderTests(t, []gridtypes.Workload{zdbWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{zdbWlCp}) _, err := state.LoadZdbFromGrid(context.Background(), 1, "test", deploymentName) assert.Error(t, err) @@ -762,7 +931,7 @@ func TestLoadZdbFromGrid(t *testing.T) { zdbWlCp := zdbWl zdbWlCp.Result.Data = nil - state := SetupLoaderTests(t, []gridtypes.Workload{zdbWlCp}) + state := SetupLoaderTests(t, []zosTypes.Workload{zdbWlCp}) _, err := state.LoadZdbFromGrid(context.Background(), 1, "test", deploymentName) assert.Error(t, err) diff --git a/grid-client/workloads/deployment.go b/grid-client/workloads/deployment.go index 9a7086962..1974099d3 100644 --- a/grid-client/workloads/deployment.go +++ b/grid-client/workloads/deployment.go @@ -9,12 +9,9 @@ import ( "sort" "github.com/pkg/errors" - "github.com/threefoldtech/zos/pkg/gridtypes" - "github.com/threefoldtech/zos/pkg/gridtypes/zos" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) -const Version = 3 - var nameMatch = regexp.MustCompile("^[a-zA-Z0-9_]+$") // Deployment struct @@ -25,11 +22,13 @@ type Deployment struct { SolutionProvider *uint64 // TODO: remove NetworkName string - Disks []Disk - Zdbs []ZDB - Vms []VM - QSFS []QSFS - Volumes []Volume + + Disks []Disk + Zdbs []ZDB + Vms []VM + VmsLight []VMLight + QSFS []QSFS + Volumes []Volume // computed NodeDeploymentID map[uint32]uint64 @@ -48,6 +47,7 @@ func NewDeployment(name string, nodeID uint32, disks []Disk, zdbs []ZDB, vms []VM, + vmsLight []VMLight, QSFS []QSFS, volumes []Volume, ) Deployment { @@ -60,6 +60,7 @@ func NewDeployment(name string, nodeID uint32, Disks: disks, Zdbs: zdbs, Vms: vms, + VmsLight: vmsLight, QSFS: QSFS, Volumes: volumes, } @@ -79,12 +80,22 @@ func (d *Deployment) Validate() error { return fmt.Errorf("node ID should be a positive integer not zero") } + if len(d.Vms) > 0 && len(d.VmsLight) > 0 { + return fmt.Errorf("cannot deploy vms with vm-light on the same node") + } + for _, vm := range d.Vms { if err := vm.Validate(); err != nil { return errors.Wrapf(err, "vm '%s' is invalid", vm.Name) } } + for _, vm := range d.VmsLight { + if err := vm.Validate(); err != nil { + return errors.Wrapf(err, "vm-light '%s' is invalid", vm.Name) + } + } + for _, zdb := range d.Zdbs { if err := zdb.Validate(); err != nil { return errors.Wrapf(err, "zdb '%s' is invalid", zdb.Name) @@ -134,10 +145,15 @@ func (d *Deployment) GenerateMetadata() (string, error) { d.SolutionType = fmt.Sprintf("vm/%s", d.Name) } + typ := "vm" + if len(d.VmsLight) > 0 { + typ = "vm-light" + } + deploymentData := DeploymentData{ - Version: Version, + Version: int(Version3), Name: d.Name, - Type: "vm", + Type: typ, ProjectName: d.SolutionType, } @@ -152,6 +168,7 @@ func (d *Deployment) GenerateMetadata() (string, error) { // Nullify resets deployment func (d *Deployment) Nullify() { d.Vms = nil + d.VmsLight = nil d.QSFS = nil d.Disks = nil d.Zdbs = nil @@ -160,9 +177,11 @@ func (d *Deployment) Nullify() { } // Match objects to match the input -func (d *Deployment) Match(disks []Disk, QSFS []QSFS, zdbs []ZDB, vms []VM, volumes []Volume) { +func (d *Deployment) Match(disks []Disk, QSFS []QSFS, zdbs []ZDB, vms []VM, vmsLight []VMLight, volumes []Volume) { vmMap := make(map[string]*VM) - l := len(d.Disks) + len(d.QSFS) + len(d.Zdbs) + len(d.Vms) + len(d.Volumes) + vmLightMap := make(map[string]*VMLight) + + l := len(d.Disks) + len(d.QSFS) + len(d.Zdbs) + len(d.Vms) + len(d.VmsLight) + len(d.Volumes) names := make(map[string]int) for idx, o := range d.Disks { names[o.Name] = idx - l @@ -180,6 +199,10 @@ func (d *Deployment) Match(disks []Disk, QSFS []QSFS, zdbs []ZDB, vms []VM, volu names[o.Name] = idx - l vmMap[o.Name] = &d.Vms[idx] } + for idx, o := range d.VmsLight { + names[o.Name] = idx - l + vmLightMap[o.Name] = &d.VmsLight[idx] + } sort.Slice(disks, func(i, j int) bool { return names[disks[i].Name] < names[disks[j].Name] }) @@ -195,17 +218,26 @@ func (d *Deployment) Match(disks []Disk, QSFS []QSFS, zdbs []ZDB, vms []VM, volu sort.Slice(vms, func(i, j int) bool { return names[vms[i].Name] < names[vms[j].Name] }) + sort.Slice(vmsLight, func(i, j int) bool { + return names[vmsLight[i].Name] < names[vmsLight[j].Name] + }) for idx := range vms { vm, ok := vmMap[vms[idx].Name] if ok { vms[idx].LoadFromVM(vm) } } + for idx := range vmsLight { + vm, ok := vmLightMap[vmsLight[idx].Name] + if ok { + vmsLight[idx].LoadFromVM(vm) + } + } } // ZosDeployment generates a new zos deployment from a deployment -func (d *Deployment) ZosDeployment(twin uint32) (gridtypes.Deployment, error) { - wls := []gridtypes.Workload{} +func (d *Deployment) ZosDeployment(twin uint32) (zos.Deployment, error) { + wls := []zos.Workload{} for _, d := range d.Disks { wls = append(wls, d.ZosWorkload()) @@ -220,10 +252,15 @@ func (d *Deployment) ZosDeployment(twin uint32) (gridtypes.Deployment, error) { wls = append(wls, vmWls...) } + for _, v := range d.VmsLight { + vmLightWls := v.ZosWorkload() + wls = append(wls, vmLightWls...) + } + for _, q := range d.QSFS { qWls, err := q.ZosWorkload() if err != nil { - return gridtypes.Deployment{}, err + return zos.Deployment{}, err } wls = append(wls, qWls) } @@ -231,34 +268,19 @@ func (d *Deployment) ZosDeployment(twin uint32) (gridtypes.Deployment, error) { wls = append(wls, v.ZosWorkload()) } - return gridtypes.Deployment{ - Version: 0, - TwinID: twin, // LocalTwin, - // this contract id must match the one on substrate - ContractID: d.ContractID, - Workloads: wls, - SignatureRequirement: gridtypes.SignatureRequirement{ - WeightRequired: 1, - Requests: []gridtypes.SignatureRequest{ - { - TwinID: twin, - Weight: 1, - }, - }, - }, - }, nil + return NewGridDeployment(twin, d.ContractID, wls), nil } // NewGridDeployment generates a new grid deployment -func NewGridDeployment(twin uint32, workloads []gridtypes.Workload) gridtypes.Deployment { - return gridtypes.Deployment{ - Version: 0, - TwinID: twin, // LocalTwin, - // this contract id must match the one on substrate - Workloads: workloads, - SignatureRequirement: gridtypes.SignatureRequirement{ +func NewGridDeployment(twin uint32, contractID uint64, workloads []zos.Workload) zos.Deployment { + return zos.Deployment{ + Version: 0, + TwinID: twin, // LocalTwin, + ContractID: contractID, + Workloads: workloads, + SignatureRequirement: zos.SignatureRequirement{ WeightRequired: 1, - Requests: []gridtypes.SignatureRequest{ + Requests: []zos.SignatureRequest{ { TwinID: twin, Weight: 1, @@ -269,7 +291,7 @@ func NewGridDeployment(twin uint32, workloads []gridtypes.Workload) gridtypes.De } // GetUsedIPs returns used IPs for a deployment -func GetUsedIPs(dl gridtypes.Deployment, nodeID uint32) ([]byte, error) { +func GetUsedIPs(dl zos.Deployment, nodeID uint32) ([]byte, error) { usedIPs := []byte{} for _, w := range dl.Workloads { if !w.Result.State.IsOkay() { @@ -300,16 +322,19 @@ func ParseDeploymentData(deploymentMetaData string) (DeploymentData, error) { } // NewDeploymentFromZosDeployment generates deployment from zos deployment -func NewDeploymentFromZosDeployment(d gridtypes.Deployment, nodeID uint32) (Deployment, error) { +func NewDeploymentFromZosDeployment(d zos.Deployment, nodeID uint32) (Deployment, error) { deploymentData, err := ParseDeploymentData(d.Metadata) if err != nil { return Deployment{}, errors.Wrap(err, "failed to parse deployment data") } + vms := make([]VM, 0) + vmsLight := make([]VMLight, 0) disks := make([]Disk, 0) qs := make([]QSFS, 0) zdbs := make([]ZDB, 0) volumes := make([]Volume, 0) + var networkName string for _, workload := range d.Workloads { switch workload.Type { @@ -320,6 +345,13 @@ func NewDeploymentFromZosDeployment(d gridtypes.Deployment, nodeID uint32) (Depl } vms = append(vms, vm) networkName = vm.NetworkName + case zos.ZMachineLightType: + vmLight, err := NewVMLightFromWorkload(&workload, &d, nodeID) + if err != nil { + return Deployment{}, errors.Wrap(err, "failed to get vm-light workload") + } + vmsLight = append(vmsLight, vmLight) + networkName = vmLight.NetworkName case zos.ZDBType: zdb, err := NewZDBFromWorkload(&workload) if err != nil { @@ -353,6 +385,7 @@ func NewDeploymentFromZosDeployment(d gridtypes.Deployment, nodeID uint32) (Depl SolutionType: deploymentData.ProjectName, NetworkName: networkName, Vms: vms, + VmsLight: vmsLight, Disks: disks, QSFS: qs, Zdbs: zdbs, diff --git a/grid-client/workloads/deployment_test.go b/grid-client/workloads/deployment_test.go index 97ad31df0..67cde5231 100644 --- a/grid-client/workloads/deployment_test.go +++ b/grid-client/workloads/deployment_test.go @@ -6,17 +6,17 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/threefoldtech/zos/pkg/gridtypes" - "github.com/threefoldtech/zos/pkg/gridtypes/zos" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) func TestNewDeployment(t *testing.T) { - var zosDeployment gridtypes.Deployment + var zosDeployment zos.Deployment deployment := NewDeployment( - "test", 1, "", nil, Network.Name, + "test", 1, "", nil, n.Name, []Disk{DiskWorkload}, []ZDB{ZDBWorkload}, []VM{VMWorkload}, + []VMLight{}, []QSFS{QSFSWorkload}, []Volume{volumeWorkload}, ) @@ -30,13 +30,13 @@ func TestNewDeployment(t *testing.T) { zosDeployment, err = deployment.ZosDeployment(1) assert.NoError(t, err) - workloads := []gridtypes.Workload{DiskWorkload.ZosWorkload(), ZDBWorkload.ZosWorkload()} + workloads := []zos.Workload{DiskWorkload.ZosWorkload(), ZDBWorkload.ZosWorkload()} workloads = append(workloads, VMWorkload.ZosWorkload()...) QSFS, err := QSFSWorkload.ZosWorkload() assert.NoError(t, err) workloads = append(workloads, QSFS, volumeWorkload.ZosWorkload()) - newZosDeployment := NewGridDeployment(1, workloads) + newZosDeployment := NewGridDeployment(1, 0, workloads) assert.Equal(t, newZosDeployment, zosDeployment) }) @@ -56,7 +56,7 @@ func TestNewDeployment(t *testing.T) { t.Run("test deployment match", func(t *testing.T) { dlCp := deployment - deployment.Match([]Disk{}, []QSFS{}, []ZDB{}, []VM{}, []Volume{}) + deployment.Match([]Disk{}, []QSFS{}, []ZDB{}, []VM{}, []VMLight{}, []Volume{}) assert.Equal(t, deployment, dlCp) }) diff --git a/grid-client/workloads/disk.go b/grid-client/workloads/disk.go index 931df4ddb..6730dcd9b 100644 --- a/grid-client/workloads/disk.go +++ b/grid-client/workloads/disk.go @@ -3,7 +3,7 @@ package workloads import ( "github.com/pkg/errors" - "github.com/threefoldtech/zos/pkg/gridtypes" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "github.com/threefoldtech/zos/pkg/gridtypes/zos" ) @@ -15,10 +15,15 @@ type Disk struct { } // NewDiskFromWorkload generates a new disk from a workload -func NewDiskFromWorkload(wl *gridtypes.Workload) (Disk, error) { - dataI, err := wl.WorkloadData() +func NewDiskFromWorkload(wl *zosTypes.Workload) (Disk, error) { + var dataI interface{} + + dataI, err := wl.Workload3().WorkloadData() if err != nil { - return Disk{}, errors.Wrap(err, "failed to get workload data") + dataI, err = wl.Workload4().WorkloadData() + if err != nil { + return Disk{}, errors.Wrap(err, "failed to get workload data") + } } data, ok := dataI.(*zos.ZMount) @@ -27,21 +32,21 @@ func NewDiskFromWorkload(wl *gridtypes.Workload) (Disk, error) { } return Disk{ - Name: wl.Name.String(), + Name: wl.Name, Description: wl.Description, - SizeGB: uint64(data.Size / gridtypes.Gigabyte), + SizeGB: uint64(data.Size) / zosTypes.Gigabyte, }, nil } // ZosWorkload generates a workload from a disk -func (d *Disk) ZosWorkload() gridtypes.Workload { - return gridtypes.Workload{ - Name: gridtypes.Name(d.Name), +func (d *Disk) ZosWorkload() zosTypes.Workload { + return zosTypes.Workload{ + Name: d.Name, Version: 0, - Type: zos.ZMountType, + Type: zosTypes.ZMountType, Description: d.Description, - Data: gridtypes.MustMarshal(zos.ZMount{ - Size: gridtypes.Unit(d.SizeGB) * gridtypes.Gigabyte, + Data: zosTypes.MustMarshal(zosTypes.ZMount{ + Size: d.SizeGB * zosTypes.Gigabyte, }), } } diff --git a/grid-client/workloads/disk_test.go b/grid-client/workloads/disk_test.go index 573a37e50..d3d110046 100644 --- a/grid-client/workloads/disk_test.go +++ b/grid-client/workloads/disk_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/threefoldtech/zos/pkg/gridtypes" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) // DiskWorkload to be used for tests @@ -16,7 +16,7 @@ var DiskWorkload = Disk{ } func TestDiskWorkload(t *testing.T) { - var disk gridtypes.Workload + var disk zos.Workload t.Run("test_disk_from_map", func(t *testing.T) { diskMap, err := ToMap(DiskWorkload) diff --git a/grid-client/workloads/gateway_fqdn.go b/grid-client/workloads/gateway_fqdn.go index 1807738a4..9ea4e67d2 100644 --- a/grid-client/workloads/gateway_fqdn.go +++ b/grid-client/workloads/gateway_fqdn.go @@ -134,7 +134,7 @@ func (g *GatewayFQDNProxy) GenerateMetadata() (string, error) { } deploymentData := DeploymentData{ - Version: Version, + Version: int(Version3), Name: g.Name, Type: "Gateway Fqdn", ProjectName: g.SolutionType, diff --git a/grid-client/workloads/gateway_name.go b/grid-client/workloads/gateway_name.go index aeed86565..1e3224331 100644 --- a/grid-client/workloads/gateway_name.go +++ b/grid-client/workloads/gateway_name.go @@ -116,7 +116,7 @@ func (g *GatewayNameProxy) GenerateMetadata() (string, error) { } deploymentData := DeploymentData{ - Version: Version, + Version: int(Version3), Name: g.Name, Type: "Gateway Name", ProjectName: g.SolutionType, diff --git a/grid-client/workloads/k8s.go b/grid-client/workloads/k8s.go index ed5537a29..1f7b7097f 100644 --- a/grid-client/workloads/k8s.go +++ b/grid-client/workloads/k8s.go @@ -130,7 +130,7 @@ func (k *K8sCluster) GenerateMetadata() (string, error) { } deploymentData := DeploymentData{ - Version: Version, + Version: int(Version3), Name: k.Master.Name, Type: "kubernetes", ProjectName: k.SolutionType, @@ -252,7 +252,7 @@ func (k *K8sNode) zosWorkload(cluster *K8sCluster, isWorker bool) (K8sWorkloads publicIPName := "" if k.PublicIP || k.PublicIP6 { publicIPName = fmt.Sprintf("%sip", k.Name) - K8sWorkloads = append(K8sWorkloads, ConstructPublicIPWorkload(publicIPName, k.PublicIP, k.PublicIP6)) + K8sWorkloads = append(K8sWorkloads, ConstructK8sPublicIPWorkload(publicIPName, k.PublicIP, k.PublicIP6)) } envVars := map[string]string{ "SSH_KEY": cluster.SSHKey, @@ -305,3 +305,16 @@ func (k *K8sNode) zosWorkload(cluster *K8sCluster, isWorker bool) (K8sWorkloads return K8sWorkloads } + +// ConstructPublicIPWorkload constructs a public IP workload +func ConstructK8sPublicIPWorkload(workloadName string, ipv4 bool, ipv6 bool) gridtypes.Workload { + return gridtypes.Workload{ + Version: 0, + Name: gridtypes.Name(workloadName), + Type: zos.PublicIPType, + Data: gridtypes.MustMarshal(zos.PublicIP{ + V4: ipv4, + V6: ipv6, + }), + } +} diff --git a/grid-client/workloads/network.go b/grid-client/workloads/network.go index 8f7b732f5..ebea75ac4 100644 --- a/grid-client/workloads/network.go +++ b/grid-client/workloads/network.go @@ -6,14 +6,18 @@ import ( "crypto/rand" "encoding/json" "fmt" + r "math/rand" "net" "slices" + "sync" + "github.com/hashicorp/go-multierror" "github.com/pkg/errors" + "github.com/rs/zerolog/log" + substrate "github.com/threefoldtech/tfchain/clients/tfchain-client-go" client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/subi" - "github.com/threefoldtech/zos/pkg/gridtypes" - "github.com/threefoldtech/zos/pkg/gridtypes/zos" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) @@ -64,17 +68,17 @@ type ZNet struct { Name string Description string Nodes []uint32 - IPRange gridtypes.IPNet + IPRange zos.IPNet AddWGAccess bool MyceliumKeys map[uint32][]byte SolutionType string // computed AccessWGConfig string - ExternalIP *gridtypes.IPNet + ExternalIP *zos.IPNet ExternalSK wgtypes.Key PublicNodeID uint32 - NodesIPRange map[uint32]gridtypes.IPNet + NodesIPRange map[uint32]zos.IPNet NodeDeploymentID map[uint32]uint64 WGPort map[uint32]int @@ -82,15 +86,10 @@ type ZNet struct { } // NewNetworkFromWorkload generates a new znet from a workload -func NewNetworkFromWorkload(wl gridtypes.Workload, nodeID uint32) (ZNet, error) { - dataI, err := wl.WorkloadData() +func NewNetworkFromWorkload(wl zos.Workload, nodeID uint32) (ZNet, error) { + data, err := wl.NetworkWorkload() if err != nil { - return ZNet{}, errors.Wrap(err, "failed to get workload data") - } - - data, ok := dataI.(*zos.Network) - if !ok { - return ZNet{}, errors.Errorf("could not create network workload from data %v", dataI) + return ZNet{}, errors.Errorf("could not create network workload from data") } keys := map[uint32]wgtypes.Key{} @@ -112,10 +111,10 @@ func NewNetworkFromWorkload(wl gridtypes.Workload, nodeID uint32) (ZNet, error) return ZNet{}, errors.Wrapf(err, "failed to parse network metadata from workload %s", wl.Name) } - var externalIP *gridtypes.IPNet + var externalIP *zos.IPNet if len(metadata.UserAccesses) > 0 && metadata.UserAccesses[0].Subnet != "" { - ipNet, err := gridtypes.ParseIPNet(metadata.UserAccesses[0].Subnet) + ipNet, err := zos.ParseIPNet(metadata.UserAccesses[0].Subnet) if err != nil { return ZNet{}, err } @@ -141,11 +140,11 @@ func NewNetworkFromWorkload(wl gridtypes.Workload, nodeID uint32) (ZNet, error) } return ZNet{ - Name: wl.Name.String(), + Name: wl.Name, Description: wl.Description, Nodes: []uint32{nodeID}, - IPRange: data.NetworkIPRange, - NodesIPRange: map[uint32]gridtypes.IPNet{nodeID: data.Subnet}, + IPRange: zos.IPNet(data.NetworkIPRange), + NodesIPRange: map[uint32]zos.IPNet{nodeID: zos.IPNet(data.Subnet)}, WGPort: wgPort, Keys: keys, AddWGAccess: externalIP != nil, @@ -157,8 +156,8 @@ func NewNetworkFromWorkload(wl gridtypes.Workload, nodeID uint32) (ZNet, error) } // NewIPRange generates a new IPRange from the given network IP -func NewIPRange(n net.IPNet) gridtypes.IPNet { - return gridtypes.NewIPNet(n) +func NewIPRange(n net.IPNet) zos.IPNet { + return zos.IPNet{IPNet: n} } // Validate validates a network data @@ -189,19 +188,58 @@ func (znet *ZNet) Validate() error { return nil } +// InvalidateBrokenAttributes removes outdated attrs and deleted contracts +func (znet *ZNet) InvalidateBrokenAttributes(subConn subi.SubstrateExt, ncPool client.NodeClientGetter) error { + for node, contractID := range znet.NodeDeploymentID { + contract, err := subConn.GetContract(contractID) + if (err == nil && !contract.IsCreated()) || errors.Is(err, substrate.ErrNotFound) { + delete(znet.NodeDeploymentID, node) + delete(znet.NodesIPRange, node) + delete(znet.Keys, node) + delete(znet.WGPort, node) + } else if err != nil { + return errors.Wrapf(err, "could not get node %d contract %d", node, contractID) + } + } + if znet.ExternalIP != nil && !znet.IPRange.Contains(znet.ExternalIP.IP) { + znet.ExternalIP = nil + } + for node, ip := range znet.NodesIPRange { + if !znet.IPRange.Contains(ip.IP) { + delete(znet.NodesIPRange, node) + } + } + if znet.PublicNodeID != 0 { + // TODO: add a check that the node is still public + cl, err := ncPool.GetNodeClient(subConn, znet.PublicNodeID) + if err != nil { + // whatever the error, delete it and it will get reassigned later + znet.PublicNodeID = 0 + } + if err := cl.IsNodeUp(context.Background()); err != nil { + znet.PublicNodeID = 0 + } + } + + if !znet.AddWGAccess { + znet.ExternalIP = nil + } + return nil +} + // ZosWorkload generates a zos workload from a network -func (znet *ZNet) ZosWorkload(subnet gridtypes.IPNet, wgPrivateKey string, wgListenPort uint16, peers []zos.Peer, metadata string, myceliumKey []byte) gridtypes.Workload { +func (znet *ZNet) ZosWorkload(subnet zos.IPNet, wgPrivateKey string, wgListenPort uint16, peers []zos.Peer, metadata string, myceliumKey []byte) zos.Workload { var mycelium *zos.Mycelium if len(myceliumKey) != 0 { mycelium = &zos.Mycelium{Key: myceliumKey} } - return gridtypes.Workload{ + return zos.Workload{ Version: 0, Type: zos.NetworkType, Description: znet.Description, - Name: gridtypes.Name(znet.Name), - Data: gridtypes.MustMarshal(zos.Network{ - NetworkIPRange: gridtypes.MustParseIPNet(znet.IPRange.String()), + Name: znet.Name, + Data: zos.MustMarshal(zos.Network{ + NetworkIPRange: zos.MustParseIPNet(znet.IPRange.String()), Subnet: subnet, WGPrivateKey: wgPrivateKey, WGListenPort: wgListenPort, @@ -212,6 +250,82 @@ func (znet *ZNet) ZosWorkload(subnet gridtypes.IPNet, wgPrivateKey string, wgLis } } +func (znet *ZNet) GetVersion() Version { + return 3 +} + +func (znet *ZNet) GetNodes() []uint32 { + return znet.Nodes +} + +func (znet *ZNet) GetIPRange() zos.IPNet { + return znet.IPRange +} + +func (znet *ZNet) GetAccessWGConfig() string { + return znet.AccessWGConfig +} + +func (znet *ZNet) GetExternalIP() *zos.IPNet { + return znet.ExternalIP +} + +func (znet *ZNet) GetExternalSK() wgtypes.Key { + return znet.ExternalSK +} + +func (znet *ZNet) SetNodes(nodes []uint32) { + znet.Nodes = nodes +} + +func (znet *ZNet) GetMyceliumKeys() map[uint32][]byte { + return znet.MyceliumKeys +} + +func (znet *ZNet) SetMyceliumKeys(keys map[uint32][]byte) { + znet.MyceliumKeys = keys +} + +func (znet *ZNet) SetKeys(keys map[uint32]wgtypes.Key) { + znet.Keys = keys +} + +func (znet *ZNet) SetNodesIPRange(nodesIPRange map[uint32]zos.IPNet) { + znet.NodesIPRange = nodesIPRange +} + +func (znet *ZNet) SetWGPort(wgPort map[uint32]int) { + znet.WGPort = wgPort +} + +func (znet *ZNet) SetAccessWGConfig(accessWGConfig string) { + znet.AccessWGConfig = accessWGConfig +} + +func (znet *ZNet) GetName() string { + return znet.Name +} + +func (znet *ZNet) GetNodesIPRange() map[uint32]zos.IPNet { + return znet.NodesIPRange +} + +func (znet *ZNet) GetAddWGAccess() bool { + return znet.AddWGAccess +} + +func (znet *ZNet) GetNodeDeploymentID() map[uint32]uint64 { + return znet.NodeDeploymentID +} + +func (znet *ZNet) SetNodeDeploymentID(nodeDeploymentsIDs map[uint32]uint64) { + znet.NodeDeploymentID = nodeDeploymentsIDs +} + +func (znet *ZNet) GetPublicNodeID() uint32 { + return znet.PublicNodeID +} + // GenerateMetadata generates deployment metadata func (znet *ZNet) GenerateMetadata() (string, error) { if len(znet.SolutionType) == 0 { @@ -219,7 +333,7 @@ func (znet *ZNet) GenerateMetadata() (string, error) { } deploymentData := DeploymentData{ - Version: Version, + Version: int(Version3), Name: znet.Name, Type: "network", ProjectName: znet.SolutionType, @@ -235,7 +349,7 @@ func (znet *ZNet) GenerateMetadata() (string, error) { // AssignNodesIPs assign network nodes ips func (znet *ZNet) AssignNodesIPs(nodes []uint32) error { - ips := make(map[uint32]gridtypes.IPNet) + ips := make(map[uint32]zos.IPNet) l := len(znet.IPRange.IP) usedIPs := make([]byte, 0) // the third octet for node, ip := range znet.NodesIPRange { @@ -323,22 +437,22 @@ func (znet *ZNet) AssignNodesWGKey(nodes []uint32) error { } // IPNet returns an IP net type -func IPNet(a, b, c, d, msk byte) gridtypes.IPNet { - return gridtypes.NewIPNet(net.IPNet{ +func IPNet(a, b, c, d, msk byte) zos.IPNet { + return zos.IPNet{IPNet: net.IPNet{ IP: net.IPv4(a, b, c, d), Mask: net.CIDRMask(int(msk), 32), - }) + }} } // WgIP return wireguard IP network -func WgIP(ip gridtypes.IPNet) gridtypes.IPNet { +func WgIP(ip zos.IPNet) zos.IPNet { a := ip.IP[len(ip.IP)-3] b := ip.IP[len(ip.IP)-2] - return gridtypes.NewIPNet(net.IPNet{ + return zos.IPNet{IPNet: net.IPNet{ IP: net.IPv4(100, 64, a, b), Mask: net.CIDRMask(32, 32), - }) + }} } // GenerateWGConfig generates wireguard configs @@ -371,3 +485,337 @@ func RandomMyceliumKey() ([]byte, error) { _, err := rand.Read(key) return key, err } + +// GenerateVersionlessDeployments generates deployments for network without versions. +func (znet *ZNet) GenerateVersionlessDeployments( + ctx context.Context, + ncPool client.NodeClientGetter, + subConn subi.SubstrateExt, + twinID, publicNode uint32, + allNodes map[uint32]struct{}, + endpoints map[uint32]net.IP, + nodeUsedPorts map[uint32][]uint16, +) (map[uint32]zos.Deployment, error) { + var multiErr error + + var wg sync.WaitGroup + var mu sync.Mutex + + for nodeID := range allNodes { + wg.Add(1) + go func(nodeID uint32) { + defer wg.Done() + endpoint, usedPorts, err := getNodeEndpointAndPorts(ctx, ncPool, subConn, nodeID) + mu.Lock() + defer mu.Unlock() + if err != nil { + multiErr = multierror.Append(multiErr, err) + return + } + endpoints[nodeID] = endpoint + nodeUsedPorts[nodeID] = usedPorts + }(nodeID) + + } + wg.Wait() + + // here we check that the we managed to get a public node + // and that public node wasn't already processed. if we got + // an error while getting public node, then node id will be 0 + // and we will skip getting its data. + if _, ok := endpoints[publicNode]; !ok && publicNode != 0 { + endpoint, usedPorts, err := getNodeEndpointAndPorts(ctx, ncPool, subConn, publicNode) + if err != nil { + multiErr = multierror.Append(multiErr, err) + } else { + endpoints[publicNode] = endpoint + nodeUsedPorts[publicNode] = usedPorts + } + } + + dls, err := znet.generateDeployments(endpoints, nodeUsedPorts, publicNode, twinID) + if err != nil { + multiErr = multierror.Append(multiErr, err) + } + + return dls, multiErr +} + +func (znet *ZNet) generateDeployments(endpointIPs map[uint32]net.IP, usedPorts map[uint32][]uint16, publicNode, twinID uint32) (map[uint32]zos.Deployment, error) { + deployments := make(map[uint32]zos.Deployment) + + log.Debug().Msgf("nodes: %v", znet.Nodes) + + endpoints := make(map[uint32]string) + hiddenNodes := make([]uint32, 0) + accessibleNodes := make([]uint32, 0) + var ipv4Node uint32 + + for _, nodeID := range znet.Nodes { + if _, ok := endpointIPs[nodeID]; !ok { + // means the network has a failing node + // we will skip generating deployments for it. + return nil, fmt.Errorf("failed to process network %s", znet.Name) + } + if endpointIPs[nodeID] == nil { + hiddenNodes = append(hiddenNodes, nodeID) + } else if endpointIPs[nodeID].To4() != nil { + accessibleNodes = append(accessibleNodes, nodeID) + ipv4Node = nodeID + endpoints[nodeID] = endpointIPs[nodeID].String() + } else { + accessibleNodes = append(accessibleNodes, nodeID) + endpoints[nodeID] = fmt.Sprintf("[%s]", endpointIPs[nodeID].String()) + } + } + + needsIPv4Access := znet.AddWGAccess || (len(hiddenNodes) != 0 && len(hiddenNodes)+len(accessibleNodes) > 1) + if needsIPv4Access { + if znet.PublicNodeID != 0 { // it's set + // if public node id is already set, it should be added to accessible nodes + if !Contains(accessibleNodes, znet.PublicNodeID) { + accessibleNodes = append(accessibleNodes, znet.PublicNodeID) + } + } else if ipv4Node != 0 { // there's one in the network original nodes + znet.PublicNodeID = ipv4Node + } else if _, ok := endpointIPs[publicNode]; !ok || publicNode == 0 { + // that means either we didn't find a public node + // or we failed to get its endpoint, so can't continue with the network + return nil, fmt.Errorf("failed to get public node for %s", znet.Name) + } else { + znet.PublicNodeID = publicNode + accessibleNodes = append(accessibleNodes, publicNode) + endpoints[publicNode] = endpointIPs[publicNode].String() + } + } + + allNodes := append(hiddenNodes, accessibleNodes...) + if err := znet.AssignNodesIPs(allNodes); err != nil { + return nil, errors.Wrap(err, "could not assign node ips") + } + if err := znet.AssignNodesWGKey(allNodes); err != nil { + return nil, errors.Wrap(err, "could not assign node wg keys") + } + if znet.WGPort == nil { + znet.WGPort = make(map[uint32]int) + } + + // assign WireGuard ports + for _, nodeID := range allNodes { + nodeUsedPorts := usedPorts[nodeID] + p := uint16(r.Intn(32768-1024) + 1024) + for slices.Contains(nodeUsedPorts, p) { + p = uint16(r.Intn(32768-1024) + 1024) + } + nodeUsedPorts = append(nodeUsedPorts, p) + usedPorts[nodeID] = nodeUsedPorts + znet.WGPort[nodeID] = int(p) + } + + nonAccessibleIPRanges := []zos.IPNet{} + for _, nodeID := range hiddenNodes { + r := znet.NodesIPRange[nodeID] + nonAccessibleIPRanges = append(nonAccessibleIPRanges, r) + nonAccessibleIPRanges = append(nonAccessibleIPRanges, WgIP(r)) + } + if znet.AddWGAccess { + r := znet.ExternalIP + nonAccessibleIPRanges = append(nonAccessibleIPRanges, *r) + nonAccessibleIPRanges = append(nonAccessibleIPRanges, WgIP(*r)) + } + + log.Debug().Msgf("hidden nodes: %v", hiddenNodes) + log.Debug().Uint32("public node", znet.PublicNodeID) + log.Debug().Msgf("accessible nodes: %v", accessibleNodes) + log.Debug().Msgf("non accessible ip ranges: %v", nonAccessibleIPRanges) + + if znet.AddWGAccess { + // if no wg private key, it should be generated + if znet.ExternalSK.String() == ExternalSKZeroValue { + wgSK, err := wgtypes.GeneratePrivateKey() + if err != nil { + return nil, errors.Wrapf(err, "failed to generate wireguard secret key for network: %s", znet.Name) + } + znet.ExternalSK = wgSK + } + + znet.AccessWGConfig = GenerateWGConfig( + WgIP(*znet.ExternalIP).IP.String(), + znet.ExternalSK.String(), + znet.Keys[znet.PublicNodeID].PublicKey().String(), + fmt.Sprintf("%s:%d", endpoints[znet.PublicNodeID], znet.WGPort[znet.PublicNodeID]), + znet.IPRange.String(), + ) + } + + externalIP := "" + if znet.ExternalIP != nil { + externalIP = znet.ExternalIP.String() + } + metadata := NetworkMetaData{ + Version: int(Version3), + UserAccesses: []UserAccess{ + { + Subnet: externalIP, + PrivateKey: znet.ExternalSK.String(), + NodeID: znet.PublicNodeID, + }, + }, + } + + metadataBytes, err := json.Marshal(metadata) + if err != nil { + return nil, errors.Wrapf(err, "failed to marshal network metadata") + } + + // accessible nodes deployments + for _, nodeID := range accessibleNodes { + peers := make([]zos.Peer, 0, len(znet.Nodes)) + for _, peerNodeID := range accessibleNodes { + if peerNodeID == nodeID { + continue + } + + peerIPRange := znet.NodesIPRange[peerNodeID] + allowedIPs := []zos.IPNet{ + peerIPRange, + WgIP(peerIPRange), + } + + if peerNodeID == znet.PublicNodeID { + allowedIPs = append(allowedIPs, nonAccessibleIPRanges...) + } + + peers = append(peers, zos.Peer{ + Subnet: znet.NodesIPRange[peerNodeID], + WGPublicKey: znet.Keys[peerNodeID].PublicKey().String(), + Endpoint: fmt.Sprintf("%s:%d", endpoints[peerNodeID], znet.WGPort[peerNodeID]), + AllowedIPs: allowedIPs, + }) + } + + if nodeID == znet.PublicNodeID { + // external node + if znet.AddWGAccess { + peers = append(peers, zos.Peer{ + Subnet: *znet.ExternalIP, + WGPublicKey: znet.ExternalSK.PublicKey().String(), + AllowedIPs: []zos.IPNet{*znet.ExternalIP, WgIP(*znet.ExternalIP)}, + }) + } + + // hidden nodes + for _, peerNodeID := range hiddenNodes { + peerIPRange := znet.NodesIPRange[peerNodeID] + peers = append(peers, zos.Peer{ + Subnet: peerIPRange, + WGPublicKey: znet.Keys[peerNodeID].PublicKey().String(), + AllowedIPs: []zos.IPNet{ + peerIPRange, + WgIP(peerIPRange), + }, + }) + } + } + + workload := znet.ZosWorkload(znet.NodesIPRange[nodeID], znet.Keys[nodeID].String(), uint16(znet.WGPort[nodeID]), peers, string(metadataBytes), znet.MyceliumKeys[nodeID]) + deployment := zos.NewGridDeployment(twinID, []zos.Workload{workload}) + + // add metadata + deployment.Metadata, err = znet.GenerateMetadata() + if err != nil { + return nil, errors.Wrapf(err, "failed to generate deployment %s metadata", znet.Name) + } + + deployments[nodeID] = deployment + } + + // hidden nodes deployments + for _, nodeID := range hiddenNodes { + peers := make([]zos.Peer, 0) + if znet.PublicNodeID != 0 { + peers = append(peers, zos.Peer{ + WGPublicKey: znet.Keys[znet.PublicNodeID].PublicKey().String(), + Subnet: znet.NodesIPRange[nodeID], + AllowedIPs: []zos.IPNet{ + znet.IPRange, + IPNet(100, 64, 0, 0, 16), + }, + Endpoint: fmt.Sprintf("%s:%d", endpoints[znet.PublicNodeID], znet.WGPort[znet.PublicNodeID]), + }) + } + workload := znet.ZosWorkload(znet.NodesIPRange[nodeID], znet.Keys[nodeID].String(), uint16(znet.WGPort[nodeID]), peers, string(metadataBytes), znet.MyceliumKeys[nodeID]) + deployment := zos.NewGridDeployment(twinID, []zos.Workload{workload}) + + // add metadata + var err error + deployment.Metadata, err = znet.GenerateMetadata() + if err != nil { + return nil, errors.Wrapf(err, "failed to generate deployment %s metadata", znet.Name) + } + + deployments[nodeID] = deployment + } + return deployments, nil +} + +func getNodeEndpointAndPorts(ctx context.Context, ncPool client.NodeClientGetter, subConn subi.SubstrateExt, nodeID uint32) (net.IP, []uint16, error) { + nodeClient, err := ncPool.GetNodeClient(subConn, nodeID) + if err != nil { + return nil, nil, fmt.Errorf("could not get node %d client: %w", nodeID, err) + } + + endpoint, err := nodeClient.GetNodeEndpoint(ctx) + if err != nil && !errors.Is(err, client.ErrNoAccessibleInterfaceFound) { + return nil, nil, fmt.Errorf("failed to get node %d endpoint: %w", nodeID, err) + } + + usedPorts, err := nodeClient.NetworkListWGPorts(ctx) + if err != nil { + return nil, nil, fmt.Errorf("failed to get node %d used ports: %w", nodeID, err) + } + return endpoint, usedPorts, nil +} + +// ReadNodesConfig reads the configuration of a network +func (znet *ZNet) ReadNodesConfig(ctx context.Context, nodeDeployments map[uint32]zos.Deployment) error { + keys := make(map[uint32]wgtypes.Key) + WGPort := make(map[uint32]int) + nodesIPRange := make(map[uint32]zos.IPNet) + + log.Debug().Msg("reading node config") + WGAccess := false + for node, dl := range nodeDeployments { + for _, wl := range dl.Workloads { + if wl.Type != zos.NetworkType { + continue + } + + d, err := wl.NetworkWorkload() + if err != nil { + return errors.Wrap(err, "could not parse workload data") + } + + WGPort[node] = int(d.WGListenPort) + keys[node], err = wgtypes.ParseKey(d.WGPrivateKey) + if err != nil { + return errors.Wrap(err, "could not parse wg private key from workload object") + } + nodesIPRange[node] = zos.IPNet(d.Subnet) + // this will fail when hidden node is supported + for _, peer := range d.Peers { + if peer.Endpoint == "" { + WGAccess = true + } + } + } + } + znet.Keys = keys + znet.WGPort = WGPort + znet.NodesIPRange = nodesIPRange + znet.AddWGAccess = WGAccess + if !WGAccess { + znet.AccessWGConfig = "" + } + return nil +} diff --git a/grid-client/workloads/network_interface.go b/grid-client/workloads/network_interface.go new file mode 100644 index 000000000..d8da08bf5 --- /dev/null +++ b/grid-client/workloads/network_interface.go @@ -0,0 +1,58 @@ +package workloads + +import ( + "context" + "net" + + client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/subi" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" +) + +type Version int + +const ( + Version3 Version = 3 + Version4 Version = 4 +) + +type Network interface { + Validate() error + GetVersion() Version + ZosWorkload(subnet zos.IPNet, wgPrivateKey string, wgListenPort uint16, peers []zos.Peer, metadata string, myceliumKey []byte) zos.Workload + GenerateMetadata() (string, error) + InvalidateBrokenAttributes(subConn subi.SubstrateExt, ncPool client.NodeClientGetter) error + GenerateVersionlessDeployments( + ctx context.Context, + ncPool client.NodeClientGetter, + subConn subi.SubstrateExt, + twinID, publicNode uint32, + allNodes map[uint32]struct{}, + endpoints map[uint32]net.IP, + nodeUsedPorts map[uint32][]uint16, + ) (map[uint32]zos.Deployment, error) + generateDeployments(endpointIPs map[uint32]net.IP, usedPorts map[uint32][]uint16, publicNode, twinID uint32, + ) (map[uint32]zos.Deployment, error) + ReadNodesConfig(ctx context.Context, nodeDeployments map[uint32]zos.Deployment) error + + GetNodes() []uint32 + GetNodeDeploymentID() map[uint32]uint64 + GetPublicNodeID() uint32 + GetNodesIPRange() map[uint32]zos.IPNet + GetName() string + GetAddWGAccess() bool + GetMyceliumKeys() map[uint32][]byte + GetIPRange() zos.IPNet + GetAccessWGConfig() string + GetExternalIP() *zos.IPNet + GetExternalSK() wgtypes.Key + + SetNodeDeploymentID(nodeDeploymentsIDs map[uint32]uint64) + SetNodes(nodes []uint32) + SetMyceliumKeys(keys map[uint32][]byte) + SetKeys(keys map[uint32]wgtypes.Key) + SetNodesIPRange(nodesIPRange map[uint32]zos.IPNet) + SetWGPort(wgPort map[uint32]int) + SetAccessWGConfig(accessWGConfig string) +} diff --git a/grid-client/workloads/network_light.go b/grid-client/workloads/network_light.go new file mode 100644 index 000000000..8dac2e4bd --- /dev/null +++ b/grid-client/workloads/network_light.go @@ -0,0 +1,334 @@ +// Package workloads includes workloads types (vm, zdb, QSFS, public IP, gateway name, gateway fqdn, disk) +package workloads + +import ( + "context" + "encoding/json" + "fmt" + "net" + "slices" + + "github.com/pkg/errors" + "github.com/rs/zerolog/log" + substrate "github.com/threefoldtech/tfchain/clients/tfchain-client-go" + client "github.com/threefoldtech/tfgrid-sdk-go/grid-client/node" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/subi" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" +) + +// ZNetLight is zos network light workload +type ZNetLight struct { + Name string + Description string + SolutionType string + Nodes []uint32 + IPRange zos.IPNet + MyceliumKeys map[uint32][]byte + + // computed + PublicNodeID uint32 + NodesIPRange map[uint32]zos.IPNet + NodeDeploymentID map[uint32]uint64 +} + +// NewNetworkFromWorkload generates a new znet from a workload +func NewNetworkLightFromWorkload(wl zos.Workload, nodeID uint32) (ZNetLight, error) { + data, err := wl.NetworkLightWorkload() + if err != nil { + return ZNetLight{}, errors.Errorf("could not create network light workload from data") + } + + metadata := NetworkMetaData{} + if err := json.Unmarshal([]byte(wl.Metadata), &metadata); err != nil { + return ZNetLight{}, errors.Wrapf(err, "failed to parse network light metadata from workload %s", wl.Name) + } + + var publicNodeID uint32 + if len(metadata.UserAccesses) > 0 { + publicNodeID = metadata.UserAccesses[0].NodeID + } + + myceliumKeys := make(map[uint32][]byte) + if data.Mycelium.Key != nil { + myceliumKeys[nodeID] = data.Mycelium.Key + } + + return ZNetLight{ + Name: wl.Name, + Description: wl.Description, + Nodes: []uint32{nodeID}, + // IPRange: data.NetworkIPRange, + NodesIPRange: map[uint32]zos.IPNet{nodeID: zos.IPNet(data.Subnet)}, + PublicNodeID: publicNodeID, + MyceliumKeys: myceliumKeys, + }, nil +} + +func (znet *ZNetLight) GetVersion() Version { + return 4 +} + +func (znet *ZNetLight) GetNodes() []uint32 { + return znet.Nodes +} + +func (znet *ZNetLight) GetIPRange() zos.IPNet { + return znet.IPRange +} + +func (znet *ZNetLight) GetAccessWGConfig() string { + return "" +} + +func (znet *ZNetLight) GetExternalIP() *zos.IPNet { + return nil +} + +func (znet *ZNetLight) GetExternalSK() wgtypes.Key { + return wgtypes.Key{} +} + +func (znet *ZNetLight) GetName() string { + return znet.Name +} + +func (znet *ZNetLight) GetNodesIPRange() map[uint32]zos.IPNet { + return znet.NodesIPRange +} + +func (znet *ZNetLight) GetNodeDeploymentID() map[uint32]uint64 { + return znet.NodeDeploymentID +} + +func (znet *ZNetLight) GetAddWGAccess() bool { + return false +} + +func (znet *ZNetLight) SetNodeDeploymentID(nodeDeploymentsIDs map[uint32]uint64) { + znet.NodeDeploymentID = nodeDeploymentsIDs +} + +func (znet *ZNetLight) SetNodes(nodes []uint32) { + znet.Nodes = nodes +} + +func (znet *ZNetLight) GetMyceliumKeys() map[uint32][]byte { + return znet.MyceliumKeys +} + +func (znet *ZNetLight) SetMyceliumKeys(keys map[uint32][]byte) { + znet.MyceliumKeys = keys +} + +func (znet *ZNetLight) GetPublicNodeID() uint32 { + return znet.PublicNodeID +} + +func (znet *ZNetLight) SetNodesIPRange(nodesIPRange map[uint32]zos.IPNet) { + znet.NodesIPRange = nodesIPRange +} + +func (znet *ZNetLight) SetKeys(keys map[uint32]wgtypes.Key) { +} + +func (znet *ZNetLight) SetWGPort(wgPort map[uint32]int) { +} + +func (znet *ZNetLight) SetAccessWGConfig(accessWGConfig string) { +} + +// Validate validates a network light data +func (znet *ZNetLight) Validate() error { + if err := validateName(znet.Name); err != nil { + return errors.Wrap(err, "network name is invalid") + } + + if len(znet.Nodes) == 0 { + return fmt.Errorf("number of nodes in znet: %s, should be nonzero positive number", znet.Name) + } + + mask := znet.IPRange.Mask + if ones, _ := mask.Size(); ones != 16 { + return errors.Errorf("subnet in ip range %s should be 16", znet.IPRange.String()) + } + + for node, key := range znet.MyceliumKeys { + if len(key) != zos.MyceliumKeyLen && len(key) != 0 { + return fmt.Errorf("invalid mycelium key length %d must be %d or empty", len(key), zos.MyceliumKeyLen) + } + + if !slices.Contains(znet.Nodes, node) { + return fmt.Errorf("invalid node %d for mycelium key, must be included in the network nodes %v", node, znet.Nodes) + } + } + + return nil +} + +// InvalidateBrokenAttributes removes outdated attrs and deleted contracts +func (znet *ZNetLight) InvalidateBrokenAttributes(subConn subi.SubstrateExt, ncPool client.NodeClientGetter) error { + for node, contractID := range znet.NodeDeploymentID { + contract, err := subConn.GetContract(contractID) + if (err == nil && !contract.IsCreated()) || errors.Is(err, substrate.ErrNotFound) { + delete(znet.NodeDeploymentID, node) + delete(znet.NodesIPRange, node) + } else if err != nil { + return errors.Wrapf(err, "could not get node %d contract %d", node, contractID) + } + } + + for node, ip := range znet.NodesIPRange { + if !znet.IPRange.Contains(ip.IP) { + delete(znet.NodesIPRange, node) + } + } + + if znet.PublicNodeID != 0 { + // TODO: add a check that the node is still public + cl, err := ncPool.GetNodeClient(subConn, znet.PublicNodeID) + if err != nil { + // whatever the error, delete it and it will get reassigned later + znet.PublicNodeID = 0 + } + if err := cl.IsNodeUp(context.Background()); err != nil { + znet.PublicNodeID = 0 + } + } + + return nil +} + +// ZosWorkload generates a zos workload from a network +func (znet *ZNetLight) ZosWorkload(subnet zos.IPNet, _ string, _ uint16, _ []zos.Peer, metadata string, myceliumKey []byte) zos.Workload { + return zos.Workload{ + Version: 0, + Type: zos.NetworkLightType, + Description: znet.Description, + Name: znet.Name, + Data: zos.MustMarshal(zos.NetworkLight{ + Subnet: subnet, + Mycelium: zos.Mycelium{ + Key: myceliumKey, + }, + }), + Metadata: metadata, + } +} + +// GenerateMetadata generates deployment metadata +func (znet *ZNetLight) GenerateMetadata() (string, error) { + if len(znet.SolutionType) == 0 { + znet.SolutionType = "Network" + } + + deploymentData := DeploymentData{ + Version: int(Version4), + Name: znet.Name, + Type: "network-light", + ProjectName: znet.SolutionType, + } + + deploymentDataBytes, err := json.Marshal(deploymentData) + if err != nil { + return "", errors.Wrapf(err, "failed to parse deployment data %v", deploymentData) + } + + return string(deploymentDataBytes), nil +} + +// AssignNodesIPs assign network nodes ips +func (znet *ZNetLight) AssignNodesIPs(nodes []uint32) error { + ips := make(map[uint32]zos.IPNet) + l := len(znet.IPRange.IP) + usedIPs := make([]byte, 0) // the third octet + for node, ip := range znet.NodesIPRange { + if Contains(nodes, node) { + usedIPs = append(usedIPs, ip.IP[l-2]) + ips[node] = ip + } + } + var cur byte = 2 + for _, nodeID := range nodes { + if _, ok := ips[nodeID]; !ok { + err := nextFreeIP(usedIPs, &cur) + if err != nil { + return err + } + usedIPs = append(usedIPs, cur) + ips[nodeID] = IPNet(znet.IPRange.IP[l-4], znet.IPRange.IP[l-3], cur, znet.IPRange.IP[l-2], 24) + } + } + znet.NodesIPRange = ips + return nil +} + +// GenerateVersionlessDeployments generates deployments for network without versions. +func (znet *ZNetLight) GenerateVersionlessDeployments( + ctx context.Context, + ncPool client.NodeClientGetter, + subConn subi.SubstrateExt, + twinID, publicNode uint32, + allNodes map[uint32]struct{}, + _ map[uint32]net.IP, + _ map[uint32][]uint16, +) (map[uint32]zos.Deployment, error) { + return znet.generateDeployments(nil, nil, publicNode, twinID) +} + +func (znet *ZNetLight) generateDeployments(_ map[uint32]net.IP, _ map[uint32][]uint16, publicNode, twinID uint32) (map[uint32]zos.Deployment, error) { + deployments := make(map[uint32]zos.Deployment) + + log.Debug().Msgf("nodes: %v", znet.Nodes) + + if err := znet.AssignNodesIPs(znet.Nodes); err != nil { + return nil, errors.Wrap(err, "could not assign node ips") + } + + metadata := NetworkMetaData{ + Version: int(Version4), + } + + metadataBytes, err := json.Marshal(metadata) + if err != nil { + return nil, errors.Wrapf(err, "failed to marshal network metadata") + } + + for _, nodeID := range znet.Nodes { + workload := znet.ZosWorkload(znet.NodesIPRange[nodeID], "", 0, nil, string(metadataBytes), znet.MyceliumKeys[nodeID]) + deployment := zos.NewGridDeployment(twinID, []zos.Workload{workload}) + + // add metadata + deployment.Metadata, err = znet.GenerateMetadata() + if err != nil { + return nil, errors.Wrapf(err, "failed to generate deployment %s metadata", znet.Name) + } + + deployments[nodeID] = deployment + } + + return deployments, nil +} + +// ReadNodesConfig reads the configuration of a network +func (znet *ZNetLight) ReadNodesConfig(ctx context.Context, nodeDeployments map[uint32]zos.Deployment) error { + nodesIPRange := make(map[uint32]zos.IPNet) + log.Debug().Msg("reading node config") + for node, dl := range nodeDeployments { + for _, wl := range dl.Workloads { + if wl.Type != zos.NetworkLightType { + continue + } + + d, err := wl.NetworkLightWorkload() + if err != nil { + return errors.Wrap(err, "could not parse workload data") + } + + nodesIPRange[node] = zos.IPNet(d.Subnet) + } + } + + znet.NodesIPRange = nodesIPRange + return nil +} diff --git a/grid-client/workloads/network_test.go b/grid-client/workloads/network_test.go index cdc5bca26..97f25e9df 100644 --- a/grid-client/workloads/network_test.go +++ b/grid-client/workloads/network_test.go @@ -8,31 +8,31 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/threefoldtech/zos/pkg/gridtypes" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) // Network -var Network = ZNet{ +var n = ZNet{ Name: "testingNetwork", Description: "network for testing", Nodes: []uint32{1}, - IPRange: gridtypes.NewIPNet(net.IPNet{ + IPRange: zos.IPNet{IPNet: net.IPNet{ IP: net.IPv4(10, 20, 0, 0), Mask: net.CIDRMask(16, 32), - }), + }}, AddWGAccess: false, } func TestNetwork(t *testing.T) { t.Run("test_ip_net", func(t *testing.T) { ip := IPNet(10, 20, 0, 0, 16) - assert.Equal(t, ip, Network.IPRange) + assert.Equal(t, ip, n.IPRange) }) t.Run("test_wg_ip", func(t *testing.T) { - wgIP := WgIP(Network.IPRange) + wgIP := WgIP(n.IPRange) - wgIPRange, err := gridtypes.ParseIPNet("100.64.20.0/32") + wgIPRange, err := zos.ParseIPNet("100.64.20.0/32") assert.NoError(t, err) assert.Equal(t, wgIP, wgIPRange) @@ -41,7 +41,7 @@ func TestNetwork(t *testing.T) { t.Run("test_generate_wg_config", func(t *testing.T) { config := GenerateWGConfig( "", "", "", "", - Network.IPRange.String(), + n.IPRange.String(), ) assert.Equal(t, config, strings.ReplaceAll(fmt.Sprintf(` @@ -53,7 +53,7 @@ func TestNetwork(t *testing.T) { AllowedIPs = %s, 100.64.0.0/16 PersistentKeepalive = 25 Endpoint = %s - `, "", "", "", Network.IPRange.String(), ""), "\t", "")+"\t", + `, "", "", "", n.IPRange.String(), ""), "\t", "")+"\t", ) }) } diff --git a/grid-client/workloads/public_ip.go b/grid-client/workloads/public_ip.go index 51de657a7..1f8e211a7 100644 --- a/grid-client/workloads/public_ip.go +++ b/grid-client/workloads/public_ip.go @@ -2,17 +2,16 @@ package workloads import ( - "github.com/threefoldtech/zos/pkg/gridtypes" - "github.com/threefoldtech/zos/pkg/gridtypes/zos" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) // ConstructPublicIPWorkload constructs a public IP workload -func ConstructPublicIPWorkload(workloadName string, ipv4 bool, ipv6 bool) gridtypes.Workload { - return gridtypes.Workload{ +func ConstructPublicIPWorkload(workloadName string, ipv4 bool, ipv6 bool) zos.Workload { + return zos.Workload{ Version: 0, - Name: gridtypes.Name(workloadName), + Name: workloadName, Type: zos.PublicIPType, - Data: gridtypes.MustMarshal(zos.PublicIP{ + Data: zos.MustMarshal(zos.PublicIP{ V4: ipv4, V6: ipv6, }), diff --git a/grid-client/workloads/public_ip_test.go b/grid-client/workloads/public_ip_test.go index fbe20152f..bf8ef48e9 100644 --- a/grid-client/workloads/public_ip_test.go +++ b/grid-client/workloads/public_ip_test.go @@ -5,14 +5,14 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/threefoldtech/zos/pkg/gridtypes" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) func TestPublicIPWorkload(t *testing.T) { - var publicIPWorkload gridtypes.Workload + var publicIPWorkload zos.Workload t.Run("test_construct_pub_ip_workload", func(t *testing.T) { publicIPWorkload = ConstructPublicIPWorkload("test", true, true) - assert.NoError(t, publicIPWorkload.Type.Valid()) + assert.Equal(t, publicIPWorkload.Type, zos.PublicIPType) }) } diff --git a/grid-client/workloads/qsfs.go b/grid-client/workloads/qsfs.go index 602857ce8..af946f5f0 100644 --- a/grid-client/workloads/qsfs.go +++ b/grid-client/workloads/qsfs.go @@ -7,7 +7,7 @@ import ( "reflect" "github.com/pkg/errors" - "github.com/threefoldtech/zos/pkg/gridtypes" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "github.com/threefoldtech/zos/pkg/gridtypes/zos" ) @@ -58,7 +58,7 @@ type Group struct { } // Backend is a zos backend -type Backend zos.ZdbBackend +type Backend zosTypes.ZdbBackend // Groups is a list of groups type Groups []Group @@ -66,25 +66,25 @@ type Groups []Group // Backends is a list of backends type Backends []Backend -func (g *Group) zosGroup() (zdbGroup zos.ZdbGroup) { +func (g *Group) zosGroup() (zdbGroup zosTypes.ZdbGroup) { for _, b := range g.Backends { zdbGroup.Backends = append(zdbGroup.Backends, b.zosBackend()) } return zdbGroup } -func (gs Groups) zosGroups() (zdbGroups []zos.ZdbGroup) { +func (gs Groups) zosGroups() (zdbGroups []zosTypes.ZdbGroup) { for _, e := range gs { zdbGroups = append(zdbGroups, e.zosGroup()) } return zdbGroups } -func (b *Backend) zosBackend() zos.ZdbBackend { - return zos.ZdbBackend(*b) +func (b *Backend) zosBackend() zosTypes.ZdbBackend { + return zosTypes.ZdbBackend(*b) } -func (bs Backends) zosBackends() (zdbBackends []zos.ZdbBackend) { +func (bs Backends) zosBackends() (zdbBackends []zosTypes.ZdbBackend) { for _, e := range bs { zdbBackends = append(zdbBackends, e.zosBackend()) } @@ -110,18 +110,14 @@ func GroupsFromZos(gs []zos.ZdbGroup) (groups Groups) { } // NewQSFSFromWorkload generates a new QSFS from a workload -func NewQSFSFromWorkload(wl *gridtypes.Workload) (QSFS, error) { - var data *zos.QuantumSafeFS - dataI, err := wl.WorkloadData() - if err != nil { - return QSFS{}, err - } - - var res zos.QuatumSafeFSResult +func NewQSFSFromWorkload(wl *zosTypes.Workload) (QSFS, error) { + var dataI interface{} - if !reflect.DeepEqual(wl.Result, gridtypes.Result{}) { - if err := wl.Result.Unmarshal(&res); err != nil { - return QSFS{}, err + dataI, err := wl.Workload3().WorkloadData() + if err != nil { + dataI, err = wl.Workload4().WorkloadData() + if err != nil { + return QSFS{}, errors.Wrap(err, "failed to get workload data") } } @@ -130,10 +126,17 @@ func NewQSFSFromWorkload(wl *gridtypes.Workload) (QSFS, error) { return QSFS{}, fmt.Errorf("could not create qsfs workload from data %v", dataI) } + var result zos.QuatumSafeFSResult + if !reflect.DeepEqual(wl.Result, zosTypes.Result{}) { + if err := wl.Result.Unmarshal(&result); err != nil { + return QSFS{}, err + } + } + return QSFS{ - Name: string(wl.Name), + Name: wl.Name, Description: wl.Description, - Cache: int(data.Cache) / int(gridtypes.Megabyte), + Cache: int(data.Cache) / int(zosTypes.Megabyte), MinimalShards: data.Config.MinimalShards, ExpectedShards: data.Config.ExpectedShards, RedundantGroups: data.Config.RedundantGroups, @@ -150,50 +153,51 @@ func NewQSFSFromWorkload(wl *gridtypes.Workload) (QSFS, error) { Backends: BackendsFromZos(data.Config.Meta.Config.Backends), }, Groups: GroupsFromZos(data.Config.Groups), - MetricsEndpoint: res.MetricsEndpoint, + MetricsEndpoint: result.MetricsEndpoint, }, nil } // ZosWorkload generates a zos workload -func (q *QSFS) ZosWorkload() (gridtypes.Workload, error) { +func (q *QSFS) ZosWorkload() (zosTypes.Workload, error) { k, err := hex.DecodeString(q.EncryptionKey) if err != nil { - return gridtypes.Workload{}, err + return zosTypes.Workload{}, err } mk, err := hex.DecodeString(q.EncryptionKey) if err != nil { - return gridtypes.Workload{}, err + return zosTypes.Workload{}, err } - workload := gridtypes.Workload{ + + workload := zosTypes.Workload{ Version: 0, - Name: gridtypes.Name(q.Name), - Type: zos.QuantumSafeFSType, + Name: q.Name, + Type: zosTypes.QuantumSafeFSType, Description: q.Description, - Data: gridtypes.MustMarshal(zos.QuantumSafeFS{ - Cache: gridtypes.Unit(uint64(q.Cache) * uint64(gridtypes.Megabyte)), - Config: zos.QuantumSafeFSConfig{ + Data: zosTypes.MustMarshal(zosTypes.QuantumSafeFS{ + Cache: uint64(q.Cache) * zosTypes.Megabyte, + Config: zosTypes.QuantumSafeFSConfig{ MinimalShards: q.MinimalShards, ExpectedShards: q.ExpectedShards, RedundantGroups: q.RedundantGroups, RedundantNodes: q.RedundantNodes, MaxZDBDataDirSize: q.MaxZDBDataDirSize, - Encryption: zos.Encryption{ - Algorithm: zos.EncryptionAlgorithm(q.EncryptionAlgorithm), - Key: zos.EncryptionKey(k), + Encryption: zosTypes.Encryption{ + Algorithm: zosTypes.EncryptionAlgorithm(q.EncryptionAlgorithm), + Key: zosTypes.EncryptionKey(k), }, - Meta: zos.QuantumSafeMeta{ + Meta: zosTypes.QuantumSafeMeta{ Type: q.Metadata.Type, - Config: zos.QuantumSafeConfig{ + Config: zosTypes.QuantumSafeConfig{ Prefix: q.Metadata.Prefix, - Encryption: zos.Encryption{ - Algorithm: zos.EncryptionAlgorithm(q.EncryptionAlgorithm), - Key: zos.EncryptionKey(mk), + Encryption: zosTypes.Encryption{ + Algorithm: zosTypes.EncryptionAlgorithm(q.EncryptionAlgorithm), + Key: zosTypes.EncryptionKey(mk), }, Backends: q.Metadata.Backends.zosBackends(), }, }, Groups: q.Groups.zosGroups(), - Compression: zos.QuantumCompression{ + Compression: zosTypes.QuantumCompression{ Algorithm: q.CompressionAlgorithm, }, }, @@ -205,17 +209,16 @@ func (q *QSFS) ZosWorkload() (gridtypes.Workload, error) { // UpdateFromWorkload updates a QSFS from a workload // TODO: no updates, should construct itself from the workload -func (q *QSFS) UpdateFromWorkload(wl *gridtypes.Workload) error { +func (q *QSFS) UpdateFromWorkload(wl *zosTypes.Workload) error { if wl == nil { q.MetricsEndpoint = "" return nil } var res zos.QuatumSafeFSResult - if !reflect.DeepEqual(wl.Result, gridtypes.Result{}) { + if !reflect.DeepEqual(wl.Result, zosTypes.Result{}) { if err := wl.Result.Unmarshal(&res); err != nil { - return errors.Wrap(err, "error unmarshalling json") - + return err } } diff --git a/grid-client/workloads/qsfs_test.go b/grid-client/workloads/qsfs_test.go index 27e3fbcbc..ae618b874 100644 --- a/grid-client/workloads/qsfs_test.go +++ b/grid-client/workloads/qsfs_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/threefoldtech/zos/pkg/gridtypes" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) // QSFSWorkload for testing @@ -36,7 +36,7 @@ var QSFSWorkload = QSFS{ } func TestQSFSWorkload(t *testing.T) { - var qsfs gridtypes.Workload + var qsfs zos.Workload t.Run("test qsfs from/to map", func(t *testing.T) { qsfsMap, err := ToMap(QSFSWorkload) diff --git a/grid-client/workloads/vm.go b/grid-client/workloads/vm.go index 881abd4e1..2e762de21 100644 --- a/grid-client/workloads/vm.go +++ b/grid-client/workloads/vm.go @@ -6,10 +6,12 @@ import ( "encoding/json" "fmt" "net" + "slices" "sort" "strings" "github.com/pkg/errors" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "github.com/threefoldtech/zos/pkg/gridtypes" "github.com/threefoldtech/zos/pkg/gridtypes/zos" ) @@ -30,7 +32,7 @@ type VM struct { IP string `json:"ip"` // used to get the same mycelium ip for the vm. MyceliumIPSeed []byte `json:"mycelium_ip_seed"` - GPUs []zos.GPU `json:"gpus"` + GPUs []zosTypes.GPU `json:"gpus"` CPU uint8 `json:"cpu"` MemoryMB uint64 `json:"memory"` RootfsSizeMB uint64 `json:"rootfs_size"` @@ -61,18 +63,13 @@ func (m *Mount) Validate() error { } // NewVMFromWorkload generates a new vm from given workloads and deployment -func NewVMFromWorkload(wl *gridtypes.Workload, dl *gridtypes.Deployment, nodeID uint32) (VM, error) { - dataI, err := wl.WorkloadData() +func NewVMFromWorkload(wl *zosTypes.Workload, dl *zosTypes.Deployment, nodeID uint32) (VM, error) { + data, err := wl.ZMachineWorkload() if err != nil { - return VM{}, errors.Wrap(err, "failed to get workload data") + return VM{}, errors.Errorf("could not create zmachine workload from data") } - data, ok := dataI.(*zos.ZMachine) - if !ok { - return VM{}, errors.Errorf("could not create vm workload from data %v", dataI) - } - - var result zos.ZMachineResult + var result zosTypes.ZMachineResult if err := json.Unmarshal(wl.Result.Data, &result); err != nil { return VM{}, errors.Wrap(err, "failed to get vm result") @@ -80,7 +77,7 @@ func NewVMFromWorkload(wl *gridtypes.Workload, dl *gridtypes.Deployment, nodeID var pubIPRes zos.PublicIPResult if !data.Network.PublicIP.IsEmpty() { - pubIPRes, err = pubIP(dl, data.Network.PublicIP) + pubIPRes, err = pubIP(dl, data.Network.PublicIP.String()) if err != nil { return VM{}, errors.Wrap(err, "failed to get public ip workload") } @@ -105,8 +102,21 @@ func NewVMFromWorkload(wl *gridtypes.Workload, dl *gridtypes.Deployment, nodeID return VM{}, errors.Wrap(err, "failed to get flist checksum") } + var dataGPUs []zosTypes.GPU + for _, g := range data.GPU { + dataGPUs = append(dataGPUs, zosTypes.GPU(g)) + } + + var dataMounts []zosTypes.MachineMount + for _, m := range data.Mounts { + dataMounts = append(dataMounts, zosTypes.MachineMount{ + Name: m.Name.String(), + Mountpoint: m.Mountpoint, + }) + } + return VM{ - Name: wl.Name.String(), + Name: wl.Name, NodeID: nodeID, Description: wl.Description, Flist: data.FList, @@ -122,30 +132,30 @@ func NewVMFromWorkload(wl *gridtypes.Workload, dl *gridtypes.Deployment, nodeID MyceliumIPSeed: myceliumIPSeed, IP: data.Network.Interfaces[0].IP.String(), CPU: data.ComputeCapacity.CPU, - GPUs: data.GPU, + GPUs: dataGPUs, MemoryMB: uint64(data.ComputeCapacity.Memory / gridtypes.Megabyte), RootfsSizeMB: uint64(data.Size / gridtypes.Megabyte), Entrypoint: data.Entrypoint, - Mounts: mounts(data.Mounts), - Zlogs: zlogs(dl, wl.Name.String()), + Mounts: mounts(dataMounts), + Zlogs: zlogs(dl, wl.Name), EnvVars: data.Env, NetworkName: string(data.Network.Interfaces[0].Network), ConsoleURL: result.ConsoleURL, }, nil } -func mounts(mounts []zos.MachineMount) []Mount { +func mounts(mounts []zosTypes.MachineMount) []Mount { var res []Mount for _, mount := range mounts { res = append(res, Mount{ - Name: mount.Name.String(), + Name: mount.Name, MountPoint: mount.Mountpoint, }) } return res } -func pubIP(dl *gridtypes.Deployment, name gridtypes.Name) (zos.PublicIPResult, error) { +func pubIP(dl *zosTypes.Deployment, name string) (zos.PublicIPResult, error) { pubIPWl, err := dl.Get(name) if err != nil || !pubIPWl.Workload.Result.State.IsOkay() { pubIPWl = nil @@ -167,8 +177,8 @@ func pubIP(dl *gridtypes.Deployment, name gridtypes.Name) (zos.PublicIPResult, e } // ZosWorkload generates zos vm workloads -func (vm *VM) ZosWorkload() []gridtypes.Workload { - var workloads []gridtypes.Workload +func (vm *VM) ZosWorkload() []zosTypes.Workload { + var workloads []zosTypes.Workload publicIPName := "" if vm.PublicIP || vm.PublicIP6 { @@ -176,43 +186,43 @@ func (vm *VM) ZosWorkload() []gridtypes.Workload { workloads = append(workloads, ConstructPublicIPWorkload(publicIPName, vm.PublicIP, vm.PublicIP6)) } - var mounts []zos.MachineMount + var mounts []zosTypes.MachineMount for _, mount := range vm.Mounts { - mounts = append(mounts, zos.MachineMount{Name: gridtypes.Name(mount.Name), Mountpoint: mount.MountPoint}) + mounts = append(mounts, zosTypes.MachineMount{Name: mount.Name, Mountpoint: mount.MountPoint}) } for _, zlog := range vm.Zlogs { zlogWorkload := zlog.ZosWorkload() workloads = append(workloads, zlogWorkload) } - var myceliumIP *zos.MyceliumIP + var myceliumIP *zosTypes.MyceliumIP if len(vm.MyceliumIPSeed) != 0 { - myceliumIP = &zos.MyceliumIP{ - Network: gridtypes.Name(vm.NetworkName), + myceliumIP = &zosTypes.MyceliumIP{ + Network: vm.NetworkName, Seed: vm.MyceliumIPSeed, } } - workload := gridtypes.Workload{ + workload := zosTypes.Workload{ Version: 0, - Name: gridtypes.Name(vm.Name), - Type: zos.ZMachineType, - Data: gridtypes.MustMarshal(zos.ZMachine{ + Name: vm.Name, + Type: zosTypes.ZMachineType, + Data: zosTypes.MustMarshal(zosTypes.ZMachine{ FList: vm.Flist, - Network: zos.MachineNetwork{ - Interfaces: []zos.MachineInterface{ + Network: zosTypes.MachineNetwork{ + Interfaces: []zosTypes.MachineInterface{ { - Network: gridtypes.Name(vm.NetworkName), + Network: vm.NetworkName, IP: net.ParseIP(vm.IP), }, }, - PublicIP: gridtypes.Name(publicIPName), + PublicIP: publicIPName, Planetary: vm.Planetary, Mycelium: myceliumIP, }, - ComputeCapacity: zos.MachineCapacity{ + ComputeCapacity: zosTypes.MachineCapacity{ CPU: vm.CPU, - Memory: gridtypes.Unit(uint(vm.MemoryMB)) * gridtypes.Megabyte, + Memory: vm.MemoryMB * uint64(gridtypes.Megabyte), }, - Size: gridtypes.Unit(vm.RootfsSizeMB) * gridtypes.Megabyte, + Size: vm.RootfsSizeMB * uint64(gridtypes.Megabyte), GPU: vm.GPUs, Entrypoint: vm.Entrypoint, Corex: vm.Corex, @@ -270,8 +280,8 @@ func (vm *VM) Validate() error { } } - if len(vm.MyceliumIPSeed) != zos.MyceliumIPSeedLen && len(vm.MyceliumIPSeed) != 0 { - return fmt.Errorf("invalid mycelium ip seed length %d must be %d or empty", len(vm.MyceliumIPSeed), zos.MyceliumIPSeedLen) + if len(vm.MyceliumIPSeed) != zosTypes.MyceliumIPSeedLen && len(vm.MyceliumIPSeed) != 0 { + return fmt.Errorf("invalid mycelium ip seed length %d must be %d or empty", len(vm.MyceliumIPSeed), zosTypes.MyceliumIPSeedLen) } for _, zlog := range vm.Zlogs { @@ -320,8 +330,57 @@ func (vm *VM) LoadFromVM(vm2 *VM) { vm.FlistChecksum = vm2.FlistChecksum } +func (vm *VM) AssignPrivateIP( + networkName, + ipRange string, + nodeID uint32, + ipRangeCIDR *net.IPNet, + ip net.IP, + curHostID byte, + usedHosts map[string]map[uint32][]byte, +) (string, error) { + vmIP := net.ParseIP(vm.IP).To4() + + // if vm private ip is given + if vmIP != nil { + vmHostID := vmIP[3] // host ID of the private ip + + nodeUsedHostIDs := usedHosts[networkName][nodeID] + + // TODO: use of a duplicate IP vs an updated vm with a new/old IP + if slices.Contains(nodeUsedHostIDs, vmHostID) { + // return "", fmt.Errorf("duplicate private ip '%v' in vm '%s' is used", vmIP, vm.Name) + return vmIP.String(), nil + } + + if !ipRangeCIDR.Contains(vmIP) { + return "", fmt.Errorf("deployment ip range '%v' doesn't contain ip '%v' for vm '%s'", ipRange, vmIP, vm.Name) + } + + usedHosts[networkName][nodeID] = append(usedHosts[networkName][nodeID], vmHostID) + return vmIP.String(), nil + } + + nodeUsedHostIDs := usedHosts[networkName][nodeID] + + // try to find available host ID in the deployment ip range + for slices.Contains(nodeUsedHostIDs, curHostID) { + if curHostID == 254 { + return "", errors.New("all 253 ips of the network are exhausted") + } + curHostID++ + } + + usedHosts[networkName][nodeID] = append(usedHosts[networkName][nodeID], curHostID) + + vmIP = ip.To4() + vmIP[3] = curHostID + + return vmIP.String(), nil +} + func RandomMyceliumIPSeed() ([]byte, error) { - key := make([]byte, zos.MyceliumIPSeedLen) + key := make([]byte, zosTypes.MyceliumIPSeedLen) _, err := rand.Read(key) return key, err } diff --git a/grid-client/workloads/vm_light.go b/grid-client/workloads/vm_light.go new file mode 100644 index 000000000..5822f8e71 --- /dev/null +++ b/grid-client/workloads/vm_light.go @@ -0,0 +1,295 @@ +// Package workloads includes workloads types (vm, zdb, QSFS, public IP, gateway name, gateway fqdn, disk) +package workloads + +import ( + "encoding/json" + "fmt" + "net" + "slices" + "sort" + "strings" + + "github.com/pkg/errors" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" + "github.com/threefoldtech/zos4/pkg/gridtypes" +) + +// VMLight is a virtual machine struct +type VMLight struct { + Name string `json:"name"` + NodeID uint32 `json:"node"` + NetworkName string `json:"network_name"` + Description string `json:"description"` + Flist string `json:"flist"` + FlistChecksum string `json:"flist_checksum"` + Entrypoint string `json:"entrypoint"` + Corex bool `json:"corex"` + IP string `json:"ip"` + // used to get the same mycelium ip for the vm. + MyceliumIPSeed []byte `json:"mycelium_ip_seed"` + GPUs []zos.GPU `json:"gpus"` + CPU uint8 `json:"cpu"` + MemoryMB uint64 `json:"memory"` + RootfsSizeMB uint64 `json:"rootfs_size"` + Mounts []Mount `json:"mounts"` + Zlogs []Zlog `json:"zlogs"` + EnvVars map[string]string `json:"env_vars"` + + // OUTPUT + MyceliumIP string `json:"mycelium_ip"` + ConsoleURL string `json:"console_url"` +} + +// NewVMLightFromWorkload generates a new vm from given workloads and deployment +func NewVMLightFromWorkload(wl *zos.Workload, dl *zos.Deployment, nodeID uint32) (VMLight, error) { + data, err := wl.ZMachineLightWorkload() + if err != nil { + return VMLight{}, errors.Errorf("could not create zmachine light workload from data") + } + + var result zos.ZMachineLightResult + + if err := json.Unmarshal(wl.Result.Data, &result); err != nil { + return VMLight{}, errors.Wrap(err, "failed to get vm result") + } + + var myceliumIPSeed []byte + if data.Network.Mycelium != nil { + myceliumIPSeed = data.Network.Mycelium.Seed + } + + flistCheckSum, err := GetFlistChecksum(data.FList) + if err != nil { + return VMLight{}, errors.Wrap(err, "failed to get flist checksum") + } + + var dataGPUs []zos.GPU + for _, g := range data.GPU { + dataGPUs = append(dataGPUs, zos.GPU(g)) + } + + var dataMounts []zos.MachineMount + for _, m := range data.Mounts { + dataMounts = append(dataMounts, zos.MachineMount{ + Name: m.Name.String(), + Mountpoint: m.Mountpoint, + }) + } + + return VMLight{ + Name: wl.Name, + NodeID: nodeID, + Description: wl.Description, + Flist: data.FList, + FlistChecksum: flistCheckSum, + Corex: data.Corex, + MyceliumIP: result.MyceliumIP, + MyceliumIPSeed: myceliumIPSeed, + IP: data.Network.Interfaces[0].IP.String(), + CPU: data.ComputeCapacity.CPU, + GPUs: dataGPUs, + MemoryMB: uint64(data.ComputeCapacity.Memory) / zos.Megabyte, + RootfsSizeMB: uint64(data.Size) / zos.Megabyte, + Entrypoint: data.Entrypoint, + Mounts: mounts(dataMounts), + Zlogs: zlogs(dl, wl.Name), + EnvVars: data.Env, + NetworkName: string(data.Network.Interfaces[0].Network), + ConsoleURL: result.ConsoleURL, + }, nil +} + +// ZosWorkload generates zos vm workloads +func (vm *VMLight) ZosWorkload() []zos.Workload { + var workloads []zos.Workload + + var mounts []zos.MachineMount + for _, mount := range vm.Mounts { + mounts = append(mounts, zos.MachineMount{Name: mount.Name, Mountpoint: mount.MountPoint}) + } + for _, zlog := range vm.Zlogs { + zlogWorkload := zlog.ZosWorkload() + workloads = append(workloads, zlogWorkload) + } + var myceliumIP *zos.MyceliumIP + if len(vm.MyceliumIPSeed) != 0 { + myceliumIP = &zos.MyceliumIP{ + Network: vm.NetworkName, + Seed: vm.MyceliumIPSeed, + } + } + workload := zos.Workload{ + Version: 0, + Name: vm.Name, + Type: zos.ZMachineLightType, + Data: zos.MustMarshal(zos.ZMachineLight{ + FList: vm.Flist, + Network: zos.MachineNetworkLight{ + Interfaces: []zos.MachineInterface{ + { + Network: vm.NetworkName, + IP: net.ParseIP(vm.IP), + }, + }, + Mycelium: myceliumIP, + }, + ComputeCapacity: zos.MachineCapacity{ + CPU: vm.CPU, + Memory: vm.MemoryMB * zos.Megabyte, + }, + Size: vm.RootfsSizeMB * zos.Megabyte, + GPU: vm.GPUs, + Entrypoint: vm.Entrypoint, + Corex: vm.Corex, + Mounts: mounts, + Env: vm.EnvVars, + }), + Description: vm.Description, + } + workloads = append(workloads, workload) + + return workloads +} + +// Validate validates a virtual machine data +func (vm *VMLight) Validate() error { + if err := validateName(vm.Name); err != nil { + return errors.Wrap(err, "virtual machine name is invalid") + } + + if err := validateName(vm.NetworkName); err != nil { + return errors.Wrap(err, "network name is invalid") + } + + if vm.NodeID == 0 { + return fmt.Errorf("node ID should be a positive integer not zero") + } + + if len(strings.TrimSpace(vm.IP)) != 0 { + if ip := net.ParseIP(vm.IP); ip == nil { + return fmt.Errorf("invalid ip '%s'", vm.IP) + } + } + + if err := ValidateFlist(vm.Flist, vm.FlistChecksum); err != nil { + return errors.Wrap(err, "flist is invalid") + } + + if vm.CPU < 1 || vm.CPU > 32 { + return errors.New("CPUs must be more than or equal to 1 and less than or equal to 32") + } + + if gridtypes.Unit(vm.MemoryMB) < 250 { + return fmt.Errorf("memory capacity can't be less that 250 MB") + } + + minRoot := vm.MinRootSize() + if vm.RootfsSizeMB != 0 && uint64(gridtypes.Unit(vm.RootfsSizeMB)*gridtypes.Megabyte) < minRoot { + return fmt.Errorf("rootfs size can't be less than %d. Set to 0 for minimum", minRoot) + } + + for _, g := range vm.GPUs { + _, _, _, err := g.Parts() + if err != nil { + return errors.Wrap(err, "failed to validate GPUs") + } + } + + if len(vm.MyceliumIPSeed) != zos.MyceliumIPSeedLen && len(vm.MyceliumIPSeed) != 0 { + return fmt.Errorf("invalid mycelium ip seed length %d must be %d or empty", len(vm.MyceliumIPSeed), zos.MyceliumIPSeedLen) + } + + for _, zlog := range vm.Zlogs { + if err := zlog.Validate(); err != nil { + return errors.Wrap(err, "invalid zlog") + } + } + + for _, mount := range vm.Mounts { + if err := mount.Validate(); err != nil { + return errors.Wrap(err, "invalid mount") + } + } + + return nil +} + +func (vm *VMLight) MinRootSize() uint64 { + // sru = (cpu * mem_in_gb) / 8 + // each 1 SRU is 50GB of storage + sru := gridtypes.Unit(vm.CPU) * gridtypes.Unit(vm.MemoryMB) / (8 * gridtypes.Gigabyte) + + if sru == 0 { + return uint64(500 * gridtypes.Megabyte) + } + + return uint64(2 * gridtypes.Gigabyte) +} + +// LoadFromVM compares the vm with another given vm +func (vm *VMLight) LoadFromVM(vm2 *VMLight) { + l := len(vm2.Zlogs) + len(vm2.Mounts) + names := make(map[string]int) + for idx, zlog := range vm2.Zlogs { + names[zlog.Output] = idx - l + } + for idx, mount := range vm2.Mounts { + names[mount.Name] = idx - l + } + sort.Slice(vm.Zlogs, func(i, j int) bool { + return names[vm.Zlogs[i].Output] < names[vm.Zlogs[j].Output] + }) + sort.Slice(vm.Mounts, func(i, j int) bool { + return names[vm.Mounts[i].Name] < names[vm.Mounts[j].Name] + }) + vm.FlistChecksum = vm2.FlistChecksum +} + +func (vm *VMLight) AssignPrivateIP( + networkName, + ipRange string, + nodeID uint32, + ipRangeCIDR *net.IPNet, + ip net.IP, + curHostID byte, + usedHosts map[string]map[uint32][]byte, +) (string, error) { + vmIP := net.ParseIP(vm.IP).To4() + + // if vm private ip is given + if vmIP != nil { + vmHostID := vmIP[3] // host ID of the private ip + + nodeUsedHostIDs := usedHosts[networkName][nodeID] + + // TODO: use of a duplicate IP vs an updated vm with a new/old IP + if slices.Contains(nodeUsedHostIDs, vmHostID) { + // return "", fmt.Errorf("duplicate private ip '%v' in vm '%s' is used", vmIP, vm.Name) + return vmIP.String(), nil + } + + if !ipRangeCIDR.Contains(vmIP) { + return "", fmt.Errorf("deployment ip range '%v' doesn't contain ip '%v' for vm '%s'", ipRange, vmIP, vm.Name) + } + + usedHosts[networkName][nodeID] = append(usedHosts[networkName][nodeID], vmHostID) + return vmIP.String(), nil + } + + nodeUsedHostIDs := usedHosts[networkName][nodeID] + + // try to find available host ID in the deployment ip range + for slices.Contains(nodeUsedHostIDs, curHostID) { + if curHostID == 254 { + return "", errors.New("all 253 ips of the network are exhausted") + } + curHostID++ + } + + usedHosts[networkName][nodeID] = append(usedHosts[networkName][nodeID], curHostID) + + vmIP = ip.To4() + vmIP[3] = curHostID + + return vmIP.String(), nil +} diff --git a/grid-client/workloads/vm_test.go b/grid-client/workloads/vm_test.go index a73c44d3c..7403ef5c2 100644 --- a/grid-client/workloads/vm_test.go +++ b/grid-client/workloads/vm_test.go @@ -6,8 +6,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/threefoldtech/zos/pkg/gridtypes" - "github.com/threefoldtech/zos/pkg/gridtypes/zos" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) // VMWorkload for tests @@ -30,23 +29,23 @@ var VMWorkload = VM{ } func TestVMWorkload(t *testing.T) { - var workloadsFromVM []gridtypes.Workload - var vmWorkload gridtypes.Workload + var workloadsFromVM []zos.Workload + var vmWorkload zos.Workload VMWorkload.Zlogs = []Zlog{ZlogWorkload} - pubIPWorkload := gridtypes.Workload{ + pubIPWorkload := zos.Workload{ Version: 0, - Name: gridtypes.Name("testip"), + Name: "testip", Type: zos.PublicIPType, - Data: gridtypes.MustMarshal(zos.PublicIP{ + Data: zos.MustMarshal(zos.PublicIP{ V4: true, V6: false, }), } pubIPWorkload.Result.State = "ok" - deployment := NewGridDeployment(1, []gridtypes.Workload{vmWorkload, pubIPWorkload}) + deployment := NewGridDeployment(1, 0, []zos.Workload{vmWorkload, pubIPWorkload}) t.Run("test vm from/to map", func(t *testing.T) { vmMap, err := ToMap(VMWorkload) @@ -83,13 +82,18 @@ func TestVMWorkload(t *testing.T) { }) t.Run("test_mounts", func(t *testing.T) { - dataI, err := vmWorkload.WorkloadData() + zosZmachine, err := vmWorkload.ZMachineWorkload() assert.NoError(t, err) - zosZmachine, ok := dataI.(*zos.ZMachine) - assert.True(t, ok) + var dataMounts []zos.MachineMount + for _, m := range zosZmachine.Mounts { + dataMounts = append(dataMounts, zos.MachineMount{ + Name: m.Name.String(), + Mountpoint: m.Mountpoint, + }) + } - mountsOfVMWorkload := mounts(zosZmachine.Mounts) + mountsOfVMWorkload := mounts(dataMounts) assert.Equal(t, mountsOfVMWorkload, VMWorkload.Mounts) }) diff --git a/grid-client/workloads/volume.go b/grid-client/workloads/volume.go index a4fff6537..8c721f7a5 100644 --- a/grid-client/workloads/volume.go +++ b/grid-client/workloads/volume.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/pkg/errors" - "github.com/threefoldtech/zos/pkg/gridtypes" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "github.com/threefoldtech/zos/pkg/gridtypes/zos" ) @@ -16,10 +16,15 @@ type Volume struct { } // NewVolumeFromWorkload generates a new volume from a workload -func NewVolumeFromWorkload(wl *gridtypes.Workload) (Volume, error) { - dataI, err := wl.WorkloadData() +func NewVolumeFromWorkload(wl *zosTypes.Workload) (Volume, error) { + var dataI interface{} + + dataI, err := wl.Workload3().WorkloadData() if err != nil { - return Volume{}, fmt.Errorf("failed to get workload data: %w", err) + dataI, err = wl.Workload4().WorkloadData() + if err != nil { + return Volume{}, errors.Wrap(err, "failed to get workload data") + } } data, ok := dataI.(*zos.Volume) @@ -28,21 +33,21 @@ func NewVolumeFromWorkload(wl *gridtypes.Workload) (Volume, error) { } return Volume{ - Name: wl.Name.String(), + Name: wl.Name, Description: wl.Description, - SizeGB: uint64(data.Size / gridtypes.Gigabyte), + SizeGB: uint64(data.Size) / zosTypes.Gigabyte, }, nil } // ZosWorkload generates a workload from a volume -func (v *Volume) ZosWorkload() gridtypes.Workload { - return gridtypes.Workload{ - Name: gridtypes.Name(v.Name), +func (v *Volume) ZosWorkload() zosTypes.Workload { + return zosTypes.Workload{ + Name: v.Name, Version: 0, - Type: zos.VolumeType, + Type: zosTypes.VolumeType, Description: v.Description, - Data: gridtypes.MustMarshal(zos.ZMount{ - Size: gridtypes.Unit(v.SizeGB) * gridtypes.Gigabyte, + Data: zosTypes.MustMarshal(zosTypes.ZMount{ + Size: v.SizeGB * zosTypes.Gigabyte, }), } } diff --git a/grid-client/workloads/volume_test.go b/grid-client/workloads/volume_test.go index 14924da82..8590bc91c 100644 --- a/grid-client/workloads/volume_test.go +++ b/grid-client/workloads/volume_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/threefoldtech/zos/pkg/gridtypes" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) var volumeWorkload = Volume{ @@ -14,7 +14,7 @@ var volumeWorkload = Volume{ } func TestVolumeWorkload(t *testing.T) { - var volume gridtypes.Workload + var volume zos.Workload t.Run("test_volume_from_map", func(t *testing.T) { volumeMap, err := ToMap(volumeWorkload) diff --git a/grid-client/workloads/zdb.go b/grid-client/workloads/zdb.go index f608c7e73..c4bcb6863 100644 --- a/grid-client/workloads/zdb.go +++ b/grid-client/workloads/zdb.go @@ -6,7 +6,7 @@ import ( "fmt" "github.com/pkg/errors" - "github.com/threefoldtech/zos/pkg/gridtypes" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "github.com/threefoldtech/zos/pkg/gridtypes/zos" ) @@ -31,10 +31,15 @@ type ZDB struct { } // NewZDBFromWorkload generates a new zdb from a workload -func NewZDBFromWorkload(wl *gridtypes.Workload) (ZDB, error) { - dataI, err := wl.WorkloadData() +func NewZDBFromWorkload(wl *zosTypes.Workload) (ZDB, error) { + var dataI interface{} + + dataI, err := wl.Workload3().WorkloadData() if err != nil { - return ZDB{}, errors.Wrap(err, "failed to get workload data") + dataI, err = wl.Workload4().WorkloadData() + if err != nil { + return ZDB{}, errors.Wrap(err, "failed to get workload data") + } } data, ok := dataI.(*zos.ZDB) @@ -42,18 +47,18 @@ func NewZDBFromWorkload(wl *gridtypes.Workload) (ZDB, error) { return ZDB{}, errors.Errorf("could not create zdb workload from data %v", dataI) } - var result zos.ZDBResult + var result zosTypes.ZDBResult if err := json.Unmarshal(wl.Result.Data, &result); err != nil { return ZDB{}, errors.Wrap(err, "failed to get zdb result") } return ZDB{ - Name: wl.Name.String(), + Name: wl.Name, Description: wl.Description, Password: data.Password, Public: data.Public, - SizeGB: uint64(data.Size / gridtypes.Gigabyte), + SizeGB: uint64(data.Size) / zosTypes.Gigabyte, Mode: data.Mode.String(), IPs: result.IPs, Port: uint32(result.Port), @@ -62,15 +67,15 @@ func NewZDBFromWorkload(wl *gridtypes.Workload) (ZDB, error) { } // ZosWorkload generates a workload from a zdb -func (z *ZDB) ZosWorkload() gridtypes.Workload { - return gridtypes.Workload{ - Name: gridtypes.Name(z.Name), - Type: zos.ZDBType, +func (z *ZDB) ZosWorkload() zosTypes.Workload { + return zosTypes.Workload{ + Name: z.Name, + Type: zosTypes.ZDBType, Description: z.Description, Version: 0, - Data: gridtypes.MustMarshal(zos.ZDB{ - Size: gridtypes.Unit(z.SizeGB) * gridtypes.Gigabyte, - Mode: zos.ZDBMode(z.Mode), + Data: zosTypes.MustMarshal(zosTypes.ZDB{ + Size: z.SizeGB * zosTypes.Gigabyte, + Mode: z.Mode, Password: z.Password, Public: z.Public, }), diff --git a/grid-client/workloads/zdb_test.go b/grid-client/workloads/zdb_test.go index 3bf83ebd9..e1d02fc7f 100644 --- a/grid-client/workloads/zdb_test.go +++ b/grid-client/workloads/zdb_test.go @@ -6,8 +6,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/threefoldtech/zos/pkg/gridtypes" - "github.com/threefoldtech/zos/pkg/gridtypes/zos" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) // ZDBWorkload for tests @@ -24,7 +23,7 @@ var ZDBWorkload = ZDB{ } func TestZDB(t *testing.T) { - var zdbWorkload gridtypes.Workload + var zdbWorkload zos.Workload t.Run("test zdb to/from map", func(t *testing.T) { zdbMap, err := ToMap(ZDBWorkload) diff --git a/grid-client/workloads/zlog.go b/grid-client/workloads/zlog.go index 38d6c343e..cc28b369c 100644 --- a/grid-client/workloads/zlog.go +++ b/grid-client/workloads/zlog.go @@ -6,7 +6,7 @@ import ( "encoding/hex" "github.com/pkg/errors" - "github.com/threefoldtech/zos/pkg/gridtypes" + zosTypes "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" "github.com/threefoldtech/zos/pkg/gridtypes/zos" ) @@ -17,31 +17,36 @@ type Zlog struct { } // ZosWorkload generates a zlog workload -func (zlog *Zlog) ZosWorkload() gridtypes.Workload { +func (zlog *Zlog) ZosWorkload() zosTypes.Workload { url := []byte(zlog.Output) urlHash := md5.Sum(url) - return gridtypes.Workload{ + return zosTypes.Workload{ Version: 0, - Name: gridtypes.Name(hex.EncodeToString(urlHash[:])), - Type: zos.ZLogsType, - Data: gridtypes.MustMarshal(zos.ZLogs{ - ZMachine: gridtypes.Name(zlog.Zmachine), + Name: hex.EncodeToString(urlHash[:]), + Type: zosTypes.ZLogsType, + Data: zosTypes.MustMarshal(zosTypes.ZLogs{ + ZMachine: zlog.Zmachine, Output: zlog.Output, }), } } -func zlogs(dl *gridtypes.Deployment, name string) []Zlog { +func zlogs(dl *zosTypes.Deployment, name string) []Zlog { var res []Zlog - for _, wl := range dl.ByType(zos.ZLogsType) { + for _, wl := range dl.ByType(zosTypes.ZLogsType) { if !wl.Result.State.IsOkay() { continue } - dataI, err := wl.WorkloadData() + var dataI interface{} + + dataI, err := wl.Workload3().WorkloadData() if err != nil { - continue + dataI, err = wl.Workload4().WorkloadData() + if err != nil { + continue + } } data, ok := dataI.(*zos.ZLogs) diff --git a/grid-client/workloads/zlog_test.go b/grid-client/workloads/zlog_test.go index 5c160a3c6..f22cbed0a 100644 --- a/grid-client/workloads/zlog_test.go +++ b/grid-client/workloads/zlog_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/threefoldtech/zos/pkg/gridtypes" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) // ZlogWorkload for tests @@ -18,7 +18,7 @@ func TestZLog(t *testing.T) { zlogWorkload := ZlogWorkload.ZosWorkload() zlogWorkload.Result.State = "ok" - deployment := NewGridDeployment(1, []gridtypes.Workload{zlogWorkload}) + deployment := NewGridDeployment(1, 0, []zos.Workload{zlogWorkload}) t.Run("test_zLogs_from_deployment", func(t *testing.T) { zlogs := zlogs(&deployment, ZlogWorkload.Zmachine) diff --git a/grid-client/zos/capacity.go b/grid-client/zos/capacity.go new file mode 100644 index 000000000..7b11a8b83 --- /dev/null +++ b/grid-client/zos/capacity.go @@ -0,0 +1,23 @@ +package zos + +type Capacity struct { + CRU uint64 `json:"cru"` + SRU uint64 `json:"sru"` + HRU uint64 `json:"hru"` + MRU uint64 `json:"mru"` + IPV4U uint64 `json:"ipv4u"` +} + +// Zero returns true if capacity is zero +func (c *Capacity) Zero() bool { + return c.CRU == 0 && c.SRU == 0 && c.HRU == 0 && c.MRU == 0 && c.IPV4U == 0 +} + +// Add increments value of capacity with o +func (c *Capacity) Add(o *Capacity) { + c.CRU += o.CRU + c.MRU += o.MRU + c.SRU += o.SRU + c.HRU += o.HRU + c.IPV4U += o.IPV4U +} diff --git a/grid-client/zos/deployment.go b/grid-client/zos/deployment.go new file mode 100644 index 000000000..3f5df9156 --- /dev/null +++ b/grid-client/zos/deployment.go @@ -0,0 +1,308 @@ +package zos + +import ( + "io" + + "github.com/threefoldtech/zos/pkg/gridtypes" + gridtypes4 "github.com/threefoldtech/zos4/pkg/gridtypes" +) + +type ZosDeployment interface { + Sign(twin uint32, sk Signer) error + Valid() error + ChallengeHash() ([]byte, error) + Challenge(w io.Writer) error +} + +type Signer interface { + Sign(msg []byte) ([]byte, error) + Type() string +} + +// Deployment structure +type Deployment struct { + // Version must be set to 0 on deployment creation. And then it has to + // be incremented with each call to update. + Version uint32 `json:"version"` + // TwinID is the id of the twin sending the deployment. A twin then can only + // `get` status about deployments he owns. + TwinID uint32 `json:"twin_id"` + // ContractID the contract must be "pre created" on substrate before the deployment is + // sent to the node. The node will then validate that this deployment hash, will match the + // hash attached to this contract. + // the flow should go as follows: + // - fill in ALL deployment details (metadata, and workloads) + // - calculate the deployment hash (by calling ChallengeHash method) + // - create the contract with the right hash + // - set the contract id on the deployment object + // - send deployment to node. + ContractID uint64 `json:"contract_id"` + // Metadata is user specific meta attached to deployment, can be used to link this + // deployment to other external systems for automation + Metadata string `json:"metadata"` + // Description is human readable description of the deployment + Description string `json:"description"` + // Expiration [deprecated] is not used + Expiration int64 `json:"expiration"` + // SignatureRequirement specifications + SignatureRequirement SignatureRequirement `json:"signature_requirement"` + // Workloads is a list of workloads associated with this deployment + Workloads []Workload `json:"workloads"` +} + +// this means that twin3 must sign + one of either (twin1 or twin2) to have the right signature weight +type SignatureRequirement struct { + Requests []SignatureRequest `json:"requests"` + WeightRequired uint `json:"weight_required"` + Signatures []Signature `json:"signatures"` + SignatureStyle string `json:"signature_style"` +} + +// SignatureRequest struct a signature request of a twin +type SignatureRequest struct { + TwinID uint32 `json:"twin_id"` + Required bool `json:"required"` + Weight uint `json:"weight"` +} + +// Signature struct +type Signature struct { + TwinID uint32 `json:"twin_id"` + Signature string `json:"signature"` + SignatureType string `json:"signature_type"` +} + +// WorkloadWithID wrapper around workload type +// that holds the global workload ID +// Note: you never need to construct this manually +type WorkloadWithID struct { + *Workload + ID WorkloadID +} + +type WorkloadID string + +func NewGridDeployment(twin uint32, workloads []Workload) Deployment { + return Deployment{ + Version: 0, + TwinID: twin, // LocalTwin, + Workloads: workloads, + SignatureRequirement: SignatureRequirement{ + WeightRequired: 1, + Requests: []SignatureRequest{ + { + TwinID: twin, + Weight: 1, + }, + }, + }, + } +} + +func (d *Deployment) Valid() error { + for _, wl := range d.Workloads { + if wl.Type == NetworkLightType || wl.Type == ZMachineLightType { + return d.zosDeployment4().Valid() + } + } + return d.zosDeployment().Valid() +} + +func (d *Deployment) Sign(twin uint32, sk Signer) error { + for _, wl := range d.Workloads { + if wl.Type == NetworkLightType || wl.Type == ZMachineLightType { + dl := d.zosDeployment4() + err := dl.Sign(twin, sk) + if err != nil { + return err + } + + d.copySignature4(dl) + return nil + } + } + + dl := d.zosDeployment() + err := dl.Sign(twin, sk) + if err != nil { + return err + } + + d.copySignature(dl) + return nil +} + +func (d *Deployment) ChallengeHash() ([]byte, error) { + for _, wl := range d.Workloads { + if wl.Type == NetworkLightType || wl.Type == ZMachineLightType { + return d.zosDeployment4().ChallengeHash() + } + } + return d.zosDeployment().ChallengeHash() +} + +func (d *Deployment) Challenge(w io.Writer) error { + for _, wl := range d.Workloads { + if wl.Type == NetworkLightType || wl.Type == ZMachineLightType { + return d.zosDeployment4().Challenge(w) + } + } + return d.zosDeployment().Challenge(w) +} + +// Get a workload by name +func (d *Deployment) Get(name string) (*WorkloadWithID, error) { + for _, wl := range d.Workloads { + if wl.Type == NetworkLightType || wl.Type == ZMachineLightType { + w, err := d.zosDeployment4().Get(gridtypes4.Name(name)) + if err != nil { + return nil, err + } + + workload := NewWorkloadFromZosWorkload4(*w.Workload) + return &WorkloadWithID{ + Workload: &workload, + ID: WorkloadID(w.ID), + }, nil + } + } + + w, err := d.zosDeployment().Get(gridtypes.Name(name)) + if err != nil { + return nil, err + } + + workload := NewWorkloadFromZosWorkload(*w.Workload) + return &WorkloadWithID{ + Workload: &workload, + ID: WorkloadID(w.ID), + }, nil +} + +func (d *Deployment) ByType(typ ...string) []*WorkloadWithID { + var workloadsWithID []*WorkloadWithID + + for _, wl := range d.Workloads { + if wl.Type == NetworkLightType || wl.Type == ZMachineLightType { + var types []gridtypes4.WorkloadType + for _, t := range typ { + types = append(types, gridtypes4.WorkloadType(t)) + } + wls := d.zosDeployment4().ByType(types...) + + for _, w := range wls { + workload := NewWorkloadFromZosWorkload4(*w.Workload) + workloadsWithID = append(workloadsWithID, &WorkloadWithID{ + Workload: &workload, + ID: WorkloadID(w.ID), + }) + } + } + } + + var types []gridtypes.WorkloadType + for _, t := range typ { + types = append(types, gridtypes.WorkloadType(t)) + } + wls := d.zosDeployment().ByType(types...) + + for _, w := range wls { + workload := NewWorkloadFromZosWorkload(*w.Workload) + workloadsWithID = append(workloadsWithID, &WorkloadWithID{ + Workload: &workload, + ID: WorkloadID(w.ID), + }) + } + + return workloadsWithID +} + +func (d *Deployment) zosDeployment() *gridtypes.Deployment { + var requests []gridtypes.SignatureRequest + var signatures []gridtypes.Signature + var workloads []gridtypes.Workload + + for _, wl := range d.Workloads { + workloads = append(workloads, *wl.Workload3()) + } + + for _, req := range d.SignatureRequirement.Requests { + requests = append(requests, gridtypes.SignatureRequest(req)) + } + + for _, sign := range d.SignatureRequirement.Signatures { + signatures = append(signatures, gridtypes.Signature(sign)) + } + + return &gridtypes.Deployment{ + Version: d.Version, + TwinID: d.TwinID, + ContractID: d.ContractID, + Metadata: d.Metadata, + Description: d.Description, + Expiration: gridtypes.Timestamp(d.Expiration), + SignatureRequirement: gridtypes.SignatureRequirement{ + Requests: requests, + WeightRequired: d.SignatureRequirement.WeightRequired, + Signatures: signatures, + SignatureStyle: gridtypes.SignatureStyle(d.SignatureRequirement.SignatureStyle), + }, + Workloads: workloads, + } +} + +func (d *Deployment) zosDeployment4() *gridtypes4.Deployment { + var requests []gridtypes4.SignatureRequest + var signatures []gridtypes4.Signature + var workloads []gridtypes4.Workload + + for _, wl := range d.Workloads { + workloads = append(workloads, *wl.Workload4()) + } + + for _, req := range d.SignatureRequirement.Requests { + requests = append(requests, gridtypes4.SignatureRequest(req)) + } + + for _, sign := range d.SignatureRequirement.Signatures { + signatures = append(signatures, gridtypes4.Signature(sign)) + } + + return &gridtypes4.Deployment{ + Version: d.Version, + TwinID: d.TwinID, + ContractID: d.ContractID, + Metadata: d.Metadata, + Description: d.Description, + Expiration: gridtypes4.Timestamp(d.Expiration), + SignatureRequirement: gridtypes4.SignatureRequirement{ + Requests: requests, + WeightRequired: d.SignatureRequirement.WeightRequired, + Signatures: signatures, + SignatureStyle: gridtypes4.SignatureStyle(d.SignatureRequirement.SignatureStyle), + }, + Workloads: workloads, + } +} + +func (d *Deployment) copySignature(dl *gridtypes.Deployment) { + for _, sign := range dl.SignatureRequirement.Signatures { + d.SignatureRequirement.Signatures = append( + d.SignatureRequirement.Signatures, Signature{ + TwinID: sign.TwinID, + Signature: sign.Signature, + SignatureType: sign.SignatureType, + }) + } +} + +func (d *Deployment) copySignature4(dl *gridtypes4.Deployment) { + for _, sign := range dl.SignatureRequirement.Signatures { + d.SignatureRequirement.Signatures = append( + d.SignatureRequirement.Signatures, Signature{ + TwinID: sign.TwinID, + Signature: sign.Signature, + SignatureType: sign.SignatureType, + }) + } +} diff --git a/grid-client/zos/ipnet.go b/grid-client/zos/ipnet.go new file mode 100644 index 000000000..543d16dea --- /dev/null +++ b/grid-client/zos/ipnet.go @@ -0,0 +1,70 @@ +package zos + +import ( + "fmt" + "net" +) + +type IPNet struct{ net.IPNet } + +// ParseIPNet parse iprange +func ParseIPNet(txt string) (r IPNet, err error) { + if len(txt) == 0 { + //empty ip net value + return r, nil + } + //fmt.Println("parsing: ", string(text)) + ip, net, err := net.ParseCIDR(txt) + if err != nil { + return r, err + } + + net.IP = ip + r.IPNet = *net + return +} + +func MustParseIPNet(txt string) IPNet { + r, err := ParseIPNet(txt) + if err != nil { + panic(err) + } + return r +} + +// UnmarshalText loads IPRange from string +func (i *IPNet) UnmarshalText(text []byte) error { + v, err := ParseIPNet(string(text)) + if err != nil { + return err + } + + i.IPNet = v.IPNet + return nil +} + +// MarshalJSON dumps iprange as a string +func (i IPNet) MarshalJSON() ([]byte, error) { + if len(i.IPNet.IP) == 0 { + return []byte(`""`), nil + } + v := fmt.Sprint("\"", i.String(), "\"") + return []byte(v), nil +} + +// MarshalJSON dumps iprange as a string +func (i IPNet) MarshalText() ([]byte, error) { + if len(i.IPNet.IP) == 0 { + return []byte{}, nil + } + return []byte(i.String()), nil +} + +func (i IPNet) String() string { + return i.IPNet.String() +} + +// Nil returns true if IPNet is not set +func (i *IPNet) Nil() bool { + return i.IP == nil && i.Mask == nil +} diff --git a/grid-client/zos/machine.go b/grid-client/zos/machine.go new file mode 100644 index 000000000..5645ed626 --- /dev/null +++ b/grid-client/zos/machine.go @@ -0,0 +1,159 @@ +package zos + +import ( + "encoding/json" + "fmt" + "net" + "strings" + + "github.com/pkg/errors" + "github.com/threefoldtech/zos/pkg/gridtypes/zos" +) + +// ZMachine reservation data +type ZMachine struct { + // Flist of the zmachine, must be a valid url to an flist. + FList string `json:"flist"` + // Network configuration for machine network + Network MachineNetwork `json:"network"` + // Size of zmachine disk + Size uint64 `json:"size"` + // ComputeCapacity configuration for machine cpu+memory + ComputeCapacity MachineCapacity `json:"compute_capacity"` + // Mounts configure mounts/disks attachments to this machine + Mounts []MachineMount `json:"mounts"` + + // following items are only available in container mode. if FList is for a container + // not a VM. + + // Entrypoint entrypoint of the container, if not set the configured one from the flist + // is going to be used + Entrypoint string `json:"entrypoint"` + // Env variables available for a container + Env map[string]string `json:"env"` + // Corex works in container mode which forces replace the + // entrypoint of the container to use `corex` + Corex bool `json:"corex"` + + // GPU attached to the VM + // the list of the GPUs ids must: + // - Exist, obviously + // - Not used by other VMs + // - Only possible on `dedicated` nodes + GPU []GPU `json:"gpu,omitempty"` +} + +// MachineNetwork structure +type MachineNetwork struct { + // PublicIP optional public IP attached to this machine. If set + // it must be a valid name of a PublicIP workload in the same deployment + PublicIP string `json:"public_ip"` + // Planetary support planetary network + Planetary bool `json:"planetary"` + + // Mycelium IP config, if planetary is true, but Mycelium is not set we fall back + // to yggdrasil support. Otherwise (if mycelium is set) a mycelium ip is used instead. + Mycelium *MyceliumIP `json:"mycelium,omitempty"` + + // Interfaces list of user znets to join + Interfaces []MachineInterface `json:"interfaces"` +} + +type MyceliumIP struct { + // Network name (znet name) to join + Network string + // Seed is a six bytes random number that is used + // as a seed to derive a vm mycelium IP. + // + // This means that a VM "ip" can be moved to another VM if needed + // by simply using the same seed. + // This of course will only work if the network mycelium setup is using + // the same HexKey + Seed Bytes `json:"hex_seed"` +} + +// MachineInterface structure +type MachineInterface struct { + // Network name (znet name) to join + Network string `json:"network"` + // IP of the zmachine on this network must be a valid Ip in the + // selected network + IP net.IP `json:"ip"` +} + +// MachineMount structure +type MachineMount struct { + // Name is name of a zmount. The name must be a valid zmount + // in the same deployment as the zmachine + Name string `json:"name"` + // MountPoint inside the container. Not used if the zmachine + // is running in a vm mode. + Mountpoint string `json:"mountpoint"` +} + +// MachineCapacity structure +type MachineCapacity struct { + CPU uint8 `json:"cpu"` + Memory uint64 `json:"memory"` +} + +type GPU string + +func (g GPU) Parts() (slot, vendor, device string, err error) { + parts := strings.Split(string(g), "/") + if len(parts) != 3 { + err = fmt.Errorf("invalid GPU id format '%s'", g) + return + } + + return parts[0], parts[1], parts[2], nil +} + +// ZMachineResult result returned by VM reservation +type ZMachineResult struct { + ID string `json:"id"` + IP string `json:"ip"` + PlanetaryIP string `json:"planetary_ip"` + MyceliumIP string `json:"mycelium_ip"` + ConsoleURL string `json:"console_url"` +} + +func (r *ZMachineResult) UnmarshalJSON(data []byte) error { + var deprecated struct { + ID string `json:"id"` + IP string `json:"ip"` + YggIP string `json:"ygg_ip"` + PlanetaryIP string `json:"planetary_ip"` + MyceliumIP string `json:"mycelium_ip"` + ConsoleURL string `json:"console_url"` + } + + if err := json.Unmarshal(data, &deprecated); err != nil { + return err + } + + r.ID = deprecated.ID + r.IP = deprecated.IP + r.PlanetaryIP = deprecated.PlanetaryIP + if deprecated.YggIP != "" { + r.PlanetaryIP = deprecated.YggIP + } + r.MyceliumIP = deprecated.MyceliumIP + r.ConsoleURL = deprecated.ConsoleURL + + return nil +} + +func (wl *Workload) ZMachineWorkload() (*zos.ZMachine, error) { + dataI, err := wl.Workload3().WorkloadData() + if err != nil { + return nil, errors.Wrap(err, "failed to get workload data") + } + + data, ok := dataI.(*zos.ZMachine) + if !ok { + return nil, errors.Errorf("could not create zmachine workload from data %v", dataI) + } + + return data, nil +} diff --git a/grid-client/zos/machine_light.go b/grid-client/zos/machine_light.go new file mode 100644 index 000000000..6bbd7952c --- /dev/null +++ b/grid-client/zos/machine_light.go @@ -0,0 +1,70 @@ +package zos + +import ( + "github.com/pkg/errors" + "github.com/threefoldtech/zos4/pkg/gridtypes/zos" +) + +type ZMachineLight struct { + // Flist of the zmachine, must be a valid url to an flist. + FList string `json:"flist"` + // Network configuration for machine network + Network MachineNetworkLight `json:"network"` + // Size of zmachine disk + Size uint64 `json:"size"` + // ComputeCapacity configuration for machine cpu+memory + ComputeCapacity MachineCapacity `json:"compute_capacity"` + // Mounts configure mounts/disks attachments to this machine + Mounts []MachineMount `json:"mounts"` + + // following items are only available in container mode. if FList is for a container + // not a VM. + + // Entrypoint entrypoint of the container, if not set the configured one from the flist + // is going to be used + Entrypoint string `json:"entrypoint"` + // Env variables available for a container + Env map[string]string `json:"env"` + // Corex works in container mode which forces replace the + // entrypoint of the container to use `corex` + Corex bool `json:"corex"` + + // GPU attached to the VM + // the list of the GPUs ids must: + // - Exist, obviously + // - Not used by other VMs + // - Only possible on `dedicated` nodes + GPU []GPU `json:"gpu,omitempty"` +} + +// MachineNetworkLight structure +type MachineNetworkLight struct { + // Mycelium IP config, if planetary is true, but Mycelium is not set we fall back + // to yggdrasil support. Otherwise (if mycelium is set) a mycelium ip is used instead. + Mycelium *MyceliumIP `json:"mycelium,omitempty"` + + // Interfaces list of user zNets to join + Interfaces []MachineInterface `json:"interfaces"` +} + +// ZMachineLightResult result returned by VM reservation +type ZMachineLightResult struct { + ID string `json:"id"` + IP string `json:"ip"` + MyceliumIP string `json:"mycelium_ip"` + ConsoleURL string `json:"console_url"` +} + +func (wl *Workload) ZMachineLightWorkload() (*zos.ZMachineLight, error) { + dataI, err := wl.Workload4().WorkloadData() + if err != nil { + return nil, errors.Wrap(err, "failed to get workload data") + } + + data, ok := dataI.(*zos.ZMachineLight) + if !ok { + return nil, errors.Errorf("could not create zmachine workload from data %v", dataI) + } + + return data, nil +} diff --git a/grid-client/zos/mount.go b/grid-client/zos/mount.go new file mode 100644 index 000000000..536db3a09 --- /dev/null +++ b/grid-client/zos/mount.go @@ -0,0 +1,9 @@ +package zos + +type ZMount struct { + Size uint64 `json:"size"` +} + +type Volume struct { + Size uint64 `json:"size"` +} diff --git a/grid-client/zos/network.go b/grid-client/zos/network.go new file mode 100644 index 000000000..5f5e8bf43 --- /dev/null +++ b/grid-client/zos/network.go @@ -0,0 +1,77 @@ +package zos + +import ( + "github.com/pkg/errors" + "github.com/threefoldtech/zos/pkg/gridtypes/zos" +) + +// Network is the description of a part of a network local to a specific node. +// A network workload defines a wireguard network that is usually spans multiple nodes. One of the nodes must work as an access node +// in other words, it must be reachable from other nodes, hence it needs to have a `PublicConfig`. +// Since the user library creates all deployments upfront then all wireguard keys, and ports must be pre-deterministic and must be +// also created upfront. +// A network structure basically must consist of +// - The network information (IP range) must be an ipv4 /16 range +// - The local (node) peer definition (subnet of the network ip range, wireguard secure key, wireguard port if any) +// - List of other peers that are part of the same network with their own config +// - For each PC or a laptop (for each wireguard peer) there must be a peer in the peer list (on all nodes) +// This is why this can get complicated. +type Network struct { + // IP range of the network, must be an IPv4 /16 + // for example a 10.1.0.0/16 + NetworkIPRange IPNet `json:"ip_range"` + + // IPV4 subnet for this network resource + // this must be a valid subnet of the entire network ip range. + // for example 10.1.1.0/24 + Subnet IPNet `json:"subnet"` + + // The private wg key of this node (this peer) which is installing this + // network workload right now. + // This has to be filled in by the user (and not generated for example) + // because other peers need to be installed as well (with this peer public key) + // hence it's easier to configure everything one time at the user side and then + // apply everything on all nodes at once + WGPrivateKey string `json:"wireguard_private_key"` + // WGListenPort is the wireguard listen port on this node. this has + // to be filled in by the user for same reason as private key (other nodes need to know about it) + // To find a free port you have to ask the node first by a call over RMB about which ports are possible + // to use. + WGListenPort uint16 `json:"wireguard_listen_port"` + + // Peers is a list of other peers in this network + Peers []Peer `json:"peers"` + + // Optional mycelium configuration. If provided + // VMs in this network can use the mycelium feature. + // if no mycelium configuration is provided, vms can't + // get mycelium IPs. + Mycelium *Mycelium `json:"mycelium,omitempty"` +} + +// Peer is the description of a peer of a NetResource +type Peer struct { + // IPV4 subnet of the network resource of the peer + Subnet IPNet `json:"subnet"` + // WGPublicKey of the peer (driven from its private key) + WGPublicKey string `json:"wireguard_public_key"` + // Allowed Ips is related to his subnet. + // todo: remove and derive from subnet + AllowedIPs []IPNet `json:"allowed_ips"` + // Entrypoint of the peer + Endpoint string `json:"endpoint"` +} + +func (wl *Workload) NetworkWorkload() (*zos.Network, error) { + dataI, err := wl.Workload3().WorkloadData() + if err != nil { + return nil, errors.Wrap(err, "failed to get workload data") + } + + data, ok := dataI.(*zos.Network) + if !ok { + return nil, errors.Errorf("could not create network workload from data %v", dataI) + } + + return data, nil +} diff --git a/grid-client/zos/network_light.go b/grid-client/zos/network_light.go new file mode 100644 index 000000000..1a7c8481b --- /dev/null +++ b/grid-client/zos/network_light.go @@ -0,0 +1,74 @@ +package zos + +import ( + "encoding/hex" + + "github.com/pkg/errors" + "github.com/threefoldtech/zos4/pkg/gridtypes/zos" +) + +// Bytes value that is represented as hex when serialized to json +type Bytes []byte + +// NetworkLight is the description of a part of a network local to a specific node. +// A network workload defines a wireguard network that is usually spans multiple nodes. One of the nodes must work as an access node +// in other words, it must be reachable from other nodes, hence it needs to have a `PublicConfig`. +// Since the user library creates all deployments upfront then all wireguard keys, and ports must be pre-deterministic and must be +// also created upfront. +// A network structure basically must consist of +// - The network information (IP range) must be an ipv4 /16 range +// - The local (node) peer definition (subnet of the network ip range, wireguard secure key, wireguard port if any) +// - List of other peers that are part of the same network with their own config +// - For each PC or a laptop (for each wireguard peer) there must be a peer in the peer list (on all nodes) +// This is why this can get complicated. +type NetworkLight struct { + // IPV4 subnet for this network resource + // this must be a valid subnet of the entire network ip range. + // for example 10.1.1.0/24 + Subnet IPNet `json:"subnet"` + + // Optional mycelium configuration. If provided + // VMs in this network can use the mycelium feature. + // if no mycelium configuration is provided, vms can't + // get mycelium IPs. + Mycelium Mycelium `json:"mycelium,omitempty"` +} + +type Mycelium struct { + // Key is the key of the mycelium peer in the mycelium node + // associated with this network. + // It's provided by the user so it can be later moved to other nodes + // without losing the key. + Key Bytes `json:"hex_key"` + // An optional mycelium peer list to be used with this node, otherwise + // the default peer list is used. + Peers []string `json:"peers"` +} + +func (h *Bytes) UnmarshalText(text []byte) error { + data, err := hex.DecodeString(string(text)) + if err != nil { + return err + } + + *h = data + return nil +} + +func (h Bytes) MarshalText() (text []byte, err error) { + return []byte(hex.EncodeToString(h)), nil +} + +func (wl *Workload) NetworkLightWorkload() (*zos.NetworkLight, error) { + dataI, err := wl.Workload4().WorkloadData() + if err != nil { + return nil, errors.Wrap(err, "failed to get workload data") + } + + data, ok := dataI.(*zos.NetworkLight) + if !ok { + return nil, errors.Errorf("could not create network workload from data %v", dataI) + } + + return data, nil +} diff --git a/grid-client/zos/public_ip.go b/grid-client/zos/public_ip.go new file mode 100644 index 000000000..8c11cdcec --- /dev/null +++ b/grid-client/zos/public_ip.go @@ -0,0 +1,10 @@ +package zos + +type PublicIP struct { + // V4 use one of the reserved Ipv4 from your contract. The Ipv4 + // itself costs money + the network traffic + V4 bool `json:"v4"` + // V6 get an ipv6 for the VM. this is for free + // but the consumed capacity (network traffic) is not + V6 bool `json:"v6"` +} diff --git a/grid-client/zos/qsfs.go b/grid-client/zos/qsfs.go new file mode 100644 index 000000000..ae50afbfc --- /dev/null +++ b/grid-client/zos/qsfs.go @@ -0,0 +1,73 @@ +package zos + +import ( + "encoding/hex" +) + +type EncryptionAlgorithm string +type EncryptionKey []byte + +func (k EncryptionKey) MarshalText() ([]byte, error) { + return []byte(hex.EncodeToString(k)), nil +} + +func (k *EncryptionKey) UnmarshalText(data []byte) error { + b, err := hex.DecodeString(string(data)) + if err != nil { + return err + } + *k = b + return nil +} + +type Encryption struct { + Algorithm EncryptionAlgorithm `json:"algorithm" toml:"algorithm"` + Key EncryptionKey `json:"key" toml:"key"` +} + +type ZdbBackend struct { + Address string `json:"address" toml:"address"` + Namespace string `json:"namespace" toml:"namespace"` + Password string `json:"password" toml:"password"` +} + +type QuantumSafeConfig struct { + Prefix string `json:"prefix" toml:"prefix"` + Encryption Encryption `json:"encryption" toml:"encryption"` + Backends []ZdbBackend `json:"backends" toml:"backends"` +} + +type QuantumSafeMeta struct { + Type string `json:"type" toml:"type"` + Config QuantumSafeConfig `json:"config" toml:"config"` +} + +type ZdbGroup struct { + Backends []ZdbBackend `json:"backends" toml:"backends"` +} + +type QuantumCompression struct { + Algorithm string `json:"algorithm" toml:"algorithm"` +} + +type QuantumSafeFSConfig struct { + MinimalShards uint32 `json:"minimal_shards" toml:"minimal_shards"` + ExpectedShards uint32 `json:"expected_shards" toml:"expected_shards"` + RedundantGroups uint32 `json:"redundant_groups" toml:"redundant_groups"` + RedundantNodes uint32 `json:"redundant_nodes" toml:"redundant_nodes"` + MaxZDBDataDirSize uint32 `json:"max_zdb_data_dir_size" toml:"max_zdb_data_dir_size"` + Encryption Encryption `json:"encryption" toml:"encryption"` + Meta QuantumSafeMeta `json:"meta" toml:"meta"` + Groups []ZdbGroup `json:"groups" toml:"groups"` + Compression QuantumCompression `json:"compression" toml:"compression"` +} + +type QuantumSafeFS struct { + Cache uint64 `json:"cache"` + Config QuantumSafeFSConfig `json:"config"` +} + +type QuatumSafeFSResult struct { + Path string `json:"path"` + MetricsEndpoint string `json:"metrics_endpoint"` +} diff --git a/grid-client/zos/types.go b/grid-client/zos/types.go new file mode 100644 index 000000000..69f17c2b0 --- /dev/null +++ b/grid-client/zos/types.go @@ -0,0 +1,61 @@ +package zos + +const ( + MyceliumKeyLen = 32 + MyceliumIPSeedLen = 6 + + // Kilobyte unit multiplier + Kilobyte uint64 = 1024 + // Megabyte unit multiplier + Megabyte uint64 = 1024 * Kilobyte + // Gigabyte unit multiplier + Gigabyte uint64 = 1024 * Megabyte + // Terabyte unit multiplier + Terabyte uint64 = 1024 * Gigabyte +) + +const ( + // ZMountType type + ZMountType string = "zmount" + // NetworkType type + NetworkType string = "network" + // NetworkLightType type + NetworkLightType string = "network-light" + // ZDBType type + ZDBType string = "zdb" + // ZMachineType type + ZMachineType string = "zmachine" + // ZMachineLightType type + ZMachineLightType string = "zmachine-light" + // VolumeType type + VolumeType string = "volume" + //PublicIPv4Type type [deprecated] + PublicIPv4Type string = "ipv4" + //PublicIPType type is the new way to assign public ips + // to a VM. this has flags (V4, and V6) that has to be set. + PublicIPType string = "ip" + // GatewayNameProxyType type + GatewayNameProxyType string = "gateway-name-proxy" + // GatewayFQDNProxyType type + GatewayFQDNProxyType string = "gateway-fqdn-proxy" + // QuantumSafeFSType type + QuantumSafeFSType string = "qsfs" + // ZLogsType type + ZLogsType string = "zlogs" +) + +const ( + // StateInit is the first state of the workload on storage + StateInit ResultState = "init" + // StateUnChanged is a special error state it means there was an error + // running the action, but this error did not break previous state. + StateUnChanged ResultState = "unchanged" + // StateError constant + StateError ResultState = "error" + // StateOk constant + StateOk ResultState = "ok" + // StateDeleted constant + StateDeleted ResultState = "deleted" + // StatePaused constant + StatePaused ResultState = "paused" +) diff --git a/grid-client/zos/workload.go b/grid-client/zos/workload.go new file mode 100644 index 000000000..b09621b06 --- /dev/null +++ b/grid-client/zos/workload.go @@ -0,0 +1,184 @@ +package zos + +import ( + "encoding/json" + "io" + + "github.com/threefoldtech/zos/pkg/gridtypes" + gridtypes4 "github.com/threefoldtech/zos4/pkg/gridtypes" +) + +type Workload struct { + // Version is version of reservation object. On deployment creation, version must be 0 + // then only workloads that need to be updated must match the version of the deployment object. + // if a deployment update message is sent to a node it does the following: + // - validate deployment version + // - check workloads list, if a version is not matching the new deployment version, the workload is untouched + // - if a workload version is same as deployment, the workload is "updated" + // - if a workload is removed, the workload is deleted. + Version uint32 `json:"version"` + //Name is unique workload name per deployment (required) + Name string `json:"name"` + // Type of the reservation (container, zdb, vm, etc...) + Type string `json:"type"` + // Data is the reservation type arguments. + Data json.RawMessage `json:"data"` + // Metadata is user specific meta attached to deployment, can be used to link this + // deployment to other external systems for automation + Metadata string `json:"metadata"` + //Description human readable description of the workload + Description string `json:"description"` + // Result of reservation, set by the node + Result Result `json:"result"` +} + +// Result is the struct filled by the node +// after a reservation object has been processed +type Result struct { + // Time when the result is sent + Created int64 `json:"created"` + // State of the deployment (ok,error) + State ResultState `json:"state"` + // if State is "error", then this field contains the error + // otherwise it's nil + Error string `json:"message"` + // Data is the information generated by the provisioning of the workload + // its type depend on the reservation type + Data json.RawMessage `json:"data"` +} + +// Unmarshal a shortcut for json.Unmarshal +func (r *Result) Unmarshal(v interface{}) error { + return json.Unmarshal(r.Data, v) +} + +// ResultState type +type ResultState string + +func (s ResultState) IsAny(state ...ResultState) bool { + for _, in := range state { + if s == in { + return true + } + } + + return false +} + +func (s ResultState) IsOkay() bool { + return s.IsAny(StateOk, StatePaused) +} + +// MustMarshal is a utility function to quickly serialize workload data +func MustMarshal(data interface{}) json.RawMessage { + bytes, err := json.Marshal(data) + if err != nil { + panic(err) + } + + return json.RawMessage(bytes) +} + +func NewWorkloadFromZosWorkload(wl gridtypes.Workload) Workload { + return Workload{ + Version: wl.Version, + Name: wl.Name.String(), + Type: wl.Type.String(), + Data: wl.Data, + Metadata: wl.Metadata, + Description: wl.Description, + Result: Result{ + Created: int64(wl.Result.Created), + State: ResultState(wl.Result.State), + Error: wl.Result.Error, + Data: wl.Result.Data, + }, + } +} + +func NewWorkloadFromZosWorkload4(wl gridtypes4.Workload) Workload { + return Workload{ + Version: wl.Version, + Name: wl.Name.String(), + Type: wl.Type.String(), + Data: wl.Data, + Metadata: wl.Metadata, + Description: wl.Description, + Result: Result{ + Created: int64(wl.Result.Created), + State: ResultState(wl.Result.State), + Error: wl.Result.Error, + Data: wl.Result.Data, + }, + } +} + +func (wl *Workload) Workload3() *gridtypes.Workload { + return &gridtypes.Workload{ + Version: wl.Version, + Name: gridtypes.Name(wl.Name), + Type: gridtypes.WorkloadType(wl.Type), + Data: wl.Data, + Metadata: wl.Metadata, + Description: wl.Description, + Result: gridtypes.Result{ + Created: gridtypes.Timestamp(wl.Result.Created), + State: gridtypes.ResultState(wl.Result.State), + Error: wl.Result.Error, + Data: wl.Result.Data, + }, + } +} + +func (wl *Workload) Workload4() *gridtypes4.Workload { + return &gridtypes4.Workload{ + Version: wl.Version, + Name: gridtypes4.Name(wl.Name), + Type: gridtypes4.WorkloadType(wl.Type), + Data: wl.Data, + Metadata: wl.Metadata, + Description: wl.Description, + Result: gridtypes4.Result{ + Created: gridtypes4.Timestamp(wl.Result.Created), + State: gridtypes4.ResultState(wl.Result.State), + Error: wl.Result.Error, + Data: wl.Result.Data, + }, + } +} + +func (wl *Workload) Challenge(w io.Writer) error { + if wl.Type == NetworkLightType || wl.Type == ZMachineLightType { + return wl.Workload4().Challenge(w) + } + + return wl.Workload3().Challenge(w) +} + +// Capacity returns the used capacity by this workload +func (wl *Workload) Capacity() (Capacity, error) { + if wl.Type == NetworkLightType || wl.Type == ZMachineLightType { + cap, err := wl.Workload4().Capacity() + return Capacity{ + CRU: cap.CRU, + SRU: uint64(cap.SRU), + HRU: uint64(cap.HRU), + MRU: uint64(cap.MRU), + IPV4U: cap.IPV4U, + }, err + } + + cap, err := wl.Workload3().Capacity() + return Capacity{ + CRU: cap.CRU, + SRU: uint64(cap.SRU), + HRU: uint64(cap.HRU), + MRU: uint64(cap.MRU), + IPV4U: cap.IPV4U, + }, err +} + +func (w Workload) WithResults(result Result) Workload { + w.Result = result + return w +} diff --git a/grid-client/zos/zdb.go b/grid-client/zos/zdb.go new file mode 100644 index 000000000..443b8cca8 --- /dev/null +++ b/grid-client/zos/zdb.go @@ -0,0 +1,17 @@ +package zos + +// ZDB namespace creation info +type ZDB struct { + Size uint64 `json:"size"` + Mode string `json:"mode"` + Password string `json:"password"` + Public bool `json:"public"` +} + +// ZDBResult is the information return to the BCDB +// after deploying a 0-db namespace +type ZDBResult struct { + Namespace string + IPs []string + Port uint +} diff --git a/grid-client/zos/zlog.go b/grid-client/zos/zlog.go new file mode 100644 index 000000000..c6b3e763a --- /dev/null +++ b/grid-client/zos/zlog.go @@ -0,0 +1,8 @@ +package zos + +type ZLogs struct { + // ZMachine stream logs for which zmachine + ZMachine string `json:"zmachine"` + // Output url + Output string `json:"output"` +} diff --git a/gridify/go.mod b/gridify/go.mod index 3997c59b5..f513bbc38 100644 --- a/gridify/go.mod +++ b/gridify/go.mod @@ -62,6 +62,7 @@ require ( github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20240827163226-d4e15e206974 // indirect github.com/threefoldtech/tfgrid-sdk-go/rmb-sdk-go v0.15.18 // indirect github.com/threefoldtech/zos v0.5.6-0.20240902110349-172a0a29a6ee // indirect + github.com/threefoldtech/zos4 v0.5.6-0.20241008102757-02d898c580c4 // indirect github.com/vedhavyas/go-subkey v1.0.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect golang.org/x/crypto v0.27.0 // indirect diff --git a/gridify/go.sum b/gridify/go.sum index bc06c5749..cf48aecfc 100644 --- a/gridify/go.sum +++ b/gridify/go.sum @@ -170,6 +170,8 @@ github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20240827163226 github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20240827163226-d4e15e206974/go.mod h1:dtDKAPiUDxAwIkfHV7xcAFZcOm+xwNIuOI1MLFS+MeQ= github.com/threefoldtech/zos v0.5.6-0.20240902110349-172a0a29a6ee h1:pqpYVM0qkXujplHNfH6w5GDqcY5sLJAgOc4/hlR6+Xw= github.com/threefoldtech/zos v0.5.6-0.20240902110349-172a0a29a6ee/go.mod h1:lut72yYMJhgK0QRvF0Wd/mB3+OfIoXWz04DQuXck3Sw= +github.com/threefoldtech/zos4 v0.5.6-0.20241008102757-02d898c580c4 h1:JCExxpPL32G7evO/+gHwlZLfAX1+l9QN9t55tnPDCp0= +github.com/threefoldtech/zos4 v0.5.6-0.20241008102757-02d898c580c4/go.mod h1:7KFtZaCcEFwQ1/cz/+hkYK616Ww04ISZgmMqLWHz6To= github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= diff --git a/gridify/internal/deployer/builder.go b/gridify/internal/deployer/builder.go index 5b165e527..3e0137873 100644 --- a/gridify/internal/deployer/builder.go +++ b/gridify/internal/deployer/builder.go @@ -83,7 +83,7 @@ func buildDeployment(vmSpec VMSpec, networkName, projectName, repoURL, deploymen NetworkName: networkName, } - dl := workloads.NewDeployment(vm.Name, node, projectName, nil, networkName, nil, nil, []workloads.VM{vm}, nil, nil) + dl := workloads.NewDeployment(vm.Name, node, projectName, nil, networkName, nil, nil, []workloads.VM{vm}, nil, nil, nil) return dl } diff --git a/tfrobot/go.mod b/tfrobot/go.mod index 0cc477653..2bc7041cd 100644 --- a/tfrobot/go.mod +++ b/tfrobot/go.mod @@ -13,7 +13,6 @@ require ( github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20240827163226-d4e15e206974 github.com/threefoldtech/tfgrid-sdk-go/grid-client v0.15.18 github.com/threefoldtech/tfgrid-sdk-go/grid-proxy v0.15.18 - github.com/threefoldtech/zos v0.5.6-0.20240902110349-172a0a29a6ee github.com/vedhavyas/go-subkey v1.0.3 golang.org/x/crypto v0.27.0 golang.org/x/sync v0.8.0 @@ -57,6 +56,8 @@ require ( github.com/rs/cors v1.10.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/threefoldtech/tfgrid-sdk-go/rmb-sdk-go v0.15.18 // indirect + github.com/threefoldtech/zos v0.5.6-0.20240902110349-172a0a29a6ee // indirect + github.com/threefoldtech/zos4 v0.5.6-0.20241008102757-02d898c580c4 // indirect golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/text v0.18.0 // indirect diff --git a/tfrobot/go.sum b/tfrobot/go.sum index e80b59662..a34ecd951 100644 --- a/tfrobot/go.sum +++ b/tfrobot/go.sum @@ -138,6 +138,8 @@ github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20240827163226 github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20240827163226-d4e15e206974/go.mod h1:dtDKAPiUDxAwIkfHV7xcAFZcOm+xwNIuOI1MLFS+MeQ= github.com/threefoldtech/zos v0.5.6-0.20240902110349-172a0a29a6ee h1:pqpYVM0qkXujplHNfH6w5GDqcY5sLJAgOc4/hlR6+Xw= github.com/threefoldtech/zos v0.5.6-0.20240902110349-172a0a29a6ee/go.mod h1:lut72yYMJhgK0QRvF0Wd/mB3+OfIoXWz04DQuXck3Sw= +github.com/threefoldtech/zos4 v0.5.6-0.20241008102757-02d898c580c4 h1:JCExxpPL32G7evO/+gHwlZLfAX1+l9QN9t55tnPDCp0= +github.com/threefoldtech/zos4 v0.5.6-0.20241008102757-02d898c580c4/go.mod h1:7KFtZaCcEFwQ1/cz/+hkYK616Ww04ISZgmMqLWHz6To= github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= diff --git a/tfrobot/pkg/deployer/deployer.go b/tfrobot/pkg/deployer/deployer.go index a40290d3c..2e6e4838b 100644 --- a/tfrobot/pkg/deployer/deployer.go +++ b/tfrobot/pkg/deployer/deployer.go @@ -17,7 +17,7 @@ import ( "github.com/rs/zerolog/log" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/deployer" "github.com/threefoldtech/tfgrid-sdk-go/grid-client/workloads" - "github.com/threefoldtech/zos/pkg/gridtypes" + "github.com/threefoldtech/tfgrid-sdk-go/grid-client/zos" ) const ( @@ -180,7 +180,7 @@ func parseVMsGroup(vms []Vms, nodeGroup string, nodesIDs []int, sshKeys map[stri } func updateFailedDeployments(ctx context.Context, tfPluginClient deployer.TFPluginClient, nodesIDs []int, groupDeployments *groupDeploymentsInfo) { - var networksToBeCanceled []*workloads.ZNet + var networksToBeCanceled []workloads.Network for idx, network := range groupDeployments.networkDeployments { if groupDeployments.vmDeployments[idx].ContractID == 0 { networksToBeCanceled = append(networksToBeCanceled, network) @@ -193,18 +193,18 @@ func updateFailedDeployments(ctx context.Context, tfPluginClient deployer.TFPlug } for idx, deployment := range groupDeployments.vmDeployments { - if deployment.ContractID == 0 || len(groupDeployments.networkDeployments[idx].NodeDeploymentID) == 0 { + if deployment.ContractID == 0 || len(groupDeployments.networkDeployments[idx].GetNodeDeploymentID()) == 0 { nodeID := uint32(nodesIDs[idx%len(nodesIDs)]) groupDeployments.vmDeployments[idx].NodeID = nodeID - groupDeployments.networkDeployments[idx].Nodes = []uint32{nodeID} + groupDeployments.networkDeployments[idx].SetNodes([]uint32{nodeID}) - myceliumKeys := groupDeployments.networkDeployments[idx].MyceliumKeys + myceliumKeys := groupDeployments.networkDeployments[idx].GetMyceliumKeys() if len(myceliumKeys) != 0 { myceliumKey, err := workloads.RandomMyceliumKey() if err != nil { log.Debug().Err(err).Send() } - groupDeployments.networkDeployments[idx].MyceliumKeys = map[uint32][]byte{nodeID: myceliumKey} + groupDeployments.networkDeployments[idx].SetMyceliumKeys(map[uint32][]byte{nodeID: myceliumKey}) } } } @@ -232,7 +232,7 @@ func massDeploy(ctx context.Context, tfPluginClient deployer.TFPluginClient, dep func buildDeployments(vms []Vms, nodesIDs []int, sshKeys map[string]string) groupDeploymentsInfo { var vmDeployments []*workloads.Deployment - var networkDeployments []*workloads.ZNet + var networkDeployments []workloads.Network var nodesIDsIdx int // here we loop over all groups of vms within the same node group, and for every group @@ -252,7 +252,7 @@ func buildDeployments(vms []Vms, nodesIDs []int, sshKeys map[string]string) grou network := buildNetworkDeployment(vmGroup, nodeID, vmName, solutionType) vm := buildVMDeployment(vmGroup, nodeID, vmName, network.Name, sshKeys[vmGroup.SSHKey], append(diskMounts, volumeMounts...)) - deployment := workloads.NewDeployment(vm.Name, nodeID, solutionType, nil, network.Name, disks, nil, []workloads.VM{vm}, nil, volumes) + deployment := workloads.NewDeployment(vm.Name, nodeID, solutionType, nil, network.Name, disks, nil, []workloads.VM{vm}, nil, nil, volumes) vmDeployments = append(vmDeployments, &deployment) networkDeployments = append(networkDeployments, &network) @@ -287,12 +287,12 @@ func parseVolumes(name string, volumes []Volume) (volWorkloads []workloads.Volum return } -func getNotDeployedDeployments(groupDeployments *groupDeploymentsInfo) ([]*workloads.ZNet, []*workloads.Deployment) { +func getNotDeployedDeployments(groupDeployments *groupDeploymentsInfo) ([]workloads.Network, []*workloads.Deployment) { var failedVmDeployments []*workloads.Deployment - var failedNetworkDeployments []*workloads.ZNet + var failedNetworkDeployments []workloads.Network for i := range groupDeployments.networkDeployments { - if len(groupDeployments.networkDeployments[i].NodeDeploymentID) == 0 { + if len(groupDeployments.networkDeployments[i].GetNodeDeploymentID()) == 0 { failedNetworkDeployments = append(failedNetworkDeployments, groupDeployments.networkDeployments[i]) } @@ -344,10 +344,10 @@ func buildNetworkDeployment(vm Vms, nodeID uint32, name, solutionType string) wo Name: fmt.Sprintf("%s_network", name), Description: "network for mass deployment", Nodes: []uint32{nodeID}, - IPRange: gridtypes.NewIPNet(net.IPNet{ + IPRange: zos.IPNet{IPNet: net.IPNet{ IP: net.IPv4(10, 20, 0, 0), Mask: net.CIDRMask(16, 32), - }), + }}, AddWGAccess: vm.WireGuard, MyceliumKeys: myceliumKeys, SolutionType: solutionType, diff --git a/tfrobot/pkg/deployer/types.go b/tfrobot/pkg/deployer/types.go index d53a43ff1..be245ee66 100644 --- a/tfrobot/pkg/deployer/types.go +++ b/tfrobot/pkg/deployer/types.go @@ -59,7 +59,7 @@ type Volume struct { type groupDeploymentsInfo struct { vmDeployments []*workloads.Deployment - networkDeployments []*workloads.ZNet + networkDeployments []workloads.Network } type vmOutput struct { diff --git a/user-contracts-mon/go.mod b/user-contracts-mon/go.mod index 29993aeed..ccd13a63d 100644 --- a/user-contracts-mon/go.mod +++ b/user-contracts-mon/go.mod @@ -43,6 +43,7 @@ require ( github.com/threefoldtech/tfgrid-sdk-go/grid-proxy v0.15.18 // indirect github.com/threefoldtech/tfgrid-sdk-go/rmb-sdk-go v0.15.18 // indirect github.com/threefoldtech/zos v0.5.6-0.20240902110349-172a0a29a6ee // indirect + github.com/threefoldtech/zos4 v0.5.6-0.20241008102757-02d898c580c4 // indirect github.com/vedhavyas/go-subkey v1.0.3 // indirect golang.org/x/crypto v0.27.0 // indirect golang.org/x/sync v0.8.0 // indirect diff --git a/user-contracts-mon/go.sum b/user-contracts-mon/go.sum index e5020fdf6..aa82ed6d0 100644 --- a/user-contracts-mon/go.sum +++ b/user-contracts-mon/go.sum @@ -108,6 +108,8 @@ github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20240827163226 github.com/threefoldtech/tfchain/clients/tfchain-client-go v0.0.0-20240827163226-d4e15e206974/go.mod h1:dtDKAPiUDxAwIkfHV7xcAFZcOm+xwNIuOI1MLFS+MeQ= github.com/threefoldtech/zos v0.5.6-0.20240902110349-172a0a29a6ee h1:pqpYVM0qkXujplHNfH6w5GDqcY5sLJAgOc4/hlR6+Xw= github.com/threefoldtech/zos v0.5.6-0.20240902110349-172a0a29a6ee/go.mod h1:lut72yYMJhgK0QRvF0Wd/mB3+OfIoXWz04DQuXck3Sw= +github.com/threefoldtech/zos4 v0.5.6-0.20241008102757-02d898c580c4 h1:JCExxpPL32G7evO/+gHwlZLfAX1+l9QN9t55tnPDCp0= +github.com/threefoldtech/zos4 v0.5.6-0.20241008102757-02d898c580c4/go.mod h1:7KFtZaCcEFwQ1/cz/+hkYK616Ww04ISZgmMqLWHz6To= github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=