From 138ffab64039c7a2133d4441dfdf4d789c573da5 Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Tue, 13 May 2025 14:09:28 +0300 Subject: [PATCH 1/4] add OpenConnections function to primitives --- pkg/primitives/statistics.go | 6 ++++-- pkg/provision.go | 3 +++ pkg/provision/engine.go | 4 ++-- pkg/provision/interface.go | 16 ++++++++------- pkg/stubs/provision_stub.go | 30 +++++++++++++++++++++++++++ pkg/stubs/statistics_stub.go | 17 +++++++++++++++ pkg/zinit/commands.go | 40 ++++++++++++++++++++++++++++++++---- 7 files changed, 101 insertions(+), 15 deletions(-) diff --git a/pkg/primitives/statistics.go b/pkg/primitives/statistics.go index e85abf08..417e863a 100644 --- a/pkg/primitives/statistics.go +++ b/pkg/primitives/statistics.go @@ -283,7 +283,6 @@ func (s *statsStream) ListGPUs() ([]pkg.GPUInfo, error) { } for _, dl := range active.Deployments { for _, wl := range dl.Workloads { - if wl.Type == zos.ZMachineType || wl.Type == zos.ZMachineLightType { var vm vmType if err := json.Unmarshal(wl.Data, &vm); err != nil { @@ -294,7 +293,6 @@ func (s *statsStream) ListGPUs() ([]pkg.GPUInfo, error) { gpus[string(gpu)] = dl.ContractID } } - } } return gpus, nil @@ -347,3 +345,7 @@ func (s *statsStream) openConnectionsCount() (int, error) { } return strconv.Atoi(strings.TrimSpace(string(out))) } + +func (s *statsStream) OpenConnections() ([]byte, error) { + return exec.Command("ss", "-ptn", "state", "established").Output() +} diff --git a/pkg/provision.go b/pkg/provision.go index 3047b3a5..1c00ade4 100644 --- a/pkg/provision.go +++ b/pkg/provision.go @@ -20,6 +20,8 @@ type Provision interface { Changes(twin uint32, contractID uint64) ([]gridtypes.Workload, error) ListPublicIPs() ([]string, error) ListPrivateIPs(twin uint32, network gridtypes.Name) ([]string, error) + Pause(twin uint32, id uint64) error + Resume(twin uint32, id uint64) error } type Statistics interface { @@ -29,6 +31,7 @@ type Statistics interface { Workloads() (int, error) GetCounters() (Counters, error) ListGPUs() ([]GPUInfo, error) + OpenConnections() ([]byte, error) } type Counters struct { diff --git a/pkg/provision/engine.go b/pkg/provision/engine.go index 14cb8800..f61f4710 100644 --- a/pkg/provision/engine.go +++ b/pkg/provision/engine.go @@ -364,7 +364,7 @@ func (e *NativeEngine) Provision(ctx context.Context, deployment gridtypes.Deplo } // Pause deployment -func (e *NativeEngine) Pause(ctx context.Context, twin uint32, id uint64) error { +func (e *NativeEngine) Pause(twin uint32, id uint64) error { deployment, err := e.storage.Get(twin, id) if err != nil { return err @@ -384,7 +384,7 @@ func (e *NativeEngine) Pause(ctx context.Context, twin uint32, id uint64) error } // Resume deployment -func (e *NativeEngine) Resume(ctx context.Context, twin uint32, id uint64) error { +func (e *NativeEngine) Resume(twin uint32, id uint64) error { deployment, err := e.storage.Get(twin, id) if err != nil { return err diff --git a/pkg/provision/interface.go b/pkg/provision/interface.go index 68fc2e44..5080bfc9 100644 --- a/pkg/provision/interface.go +++ b/pkg/provision/interface.go @@ -19,8 +19,8 @@ type Engine interface { // and will be processes later Provision(ctx context.Context, wl gridtypes.Deployment) error Deprovision(ctx context.Context, twin uint32, id uint64, reason string) error - Pause(ctx context.Context, twin uint32, id uint64) error - Resume(ctx context.Context, twin uint32, id uint64) error + Pause(twin uint32, id uint64) error + Resume(twin uint32, id uint64) error Update(ctx context.Context, update gridtypes.Deployment) error Storage() Storage Twins() Twins @@ -61,7 +61,7 @@ var ( // ErrDeploymentConflict returned if deployment cannot be stored because // it conflicts with another deployment ErrDeploymentConflict = fmt.Errorf("conflict") - //ErrDeploymentNotExists returned if object not exists + // ErrDeploymentNotExists returned if object not exists ErrDeploymentNotExists = fmt.Errorf("deployment does not exist") // ErrWorkloadNotExist returned by storage if workload does not exist ErrWorkloadNotExist = fmt.Errorf("workload does not exist") @@ -78,10 +78,12 @@ var ( ) // Field interface -type Field interface{} -type VersionField struct { - Version uint32 -} +type ( + Field interface{} + VersionField struct { + Version uint32 + } +) type DescriptionField struct { Description string diff --git a/pkg/stubs/provision_stub.go b/pkg/stubs/provision_stub.go index 859094b9..1354f438 100644 --- a/pkg/stubs/provision_stub.go +++ b/pkg/stubs/provision_stub.go @@ -159,3 +159,33 @@ func (s *ProvisionStub) ListPublicIPs(ctx context.Context) (ret0 []string, ret1 } return } + +func (s *ProvisionStub) Pause(ctx context.Context, arg0 uint32, arg1 uint64) (ret0 error) { + args := []interface{}{arg0, arg1} + result, err := s.client.RequestContext(ctx, s.module, s.object, "Pause", args...) + if err != nil { + panic(err) + } + result.PanicOnError() + ret0 = result.CallError() + loader := zbus.Loader{} + if err := result.Unmarshal(&loader); err != nil { + panic(err) + } + return +} + +func (s *ProvisionStub) Resume(ctx context.Context, arg0 uint32, arg1 uint64) (ret0 error) { + args := []interface{}{arg0, arg1} + result, err := s.client.RequestContext(ctx, s.module, s.object, "Resume", args...) + if err != nil { + panic(err) + } + result.PanicOnError() + ret0 = result.CallError() + loader := zbus.Loader{} + if err := result.Unmarshal(&loader); err != nil { + panic(err) + } + return +} diff --git a/pkg/stubs/statistics_stub.go b/pkg/stubs/statistics_stub.go index 31f66d1c..8e9311a0 100644 --- a/pkg/stubs/statistics_stub.go +++ b/pkg/stubs/statistics_stub.go @@ -79,6 +79,23 @@ func (s *StatisticsStub) ListGPUs(ctx context.Context) (ret0 []pkg.GPUInfo, ret1 return } +func (s *StatisticsStub) OpenConnections(ctx context.Context) (ret0 []uint8, ret1 error) { + args := []interface{}{} + result, err := s.client.RequestContext(ctx, s.module, s.object, "OpenConnections", args...) + if err != nil { + panic(err) + } + result.PanicOnError() + ret1 = result.CallError() + loader := zbus.Loader{ + &ret0, + } + if err := result.Unmarshal(&loader); err != nil { + panic(err) + } + return +} + func (s *StatisticsStub) ReservedStream(ctx context.Context) (<-chan gridtypes.Capacity, error) { ch := make(chan gridtypes.Capacity, 1) recv, err := s.client.Stream(ctx, s.module, s.object, "ReservedStream") diff --git a/pkg/zinit/commands.go b/pkg/zinit/commands.go index 6efd3847..8adb7543 100644 --- a/pkg/zinit/commands.go +++ b/pkg/zinit/commands.go @@ -45,9 +45,9 @@ const ( ServiceStateSuccess = "success" // ServiceStateError is return when we a service exit with an error (exit code != 0) ServiceStateError = "error" - //ServiceStateFailure is set of zinit can not spawn a service in the first place - //due to a missing executable for example. Unlike `error` which is returned if the - //service itself exits with an error. + // ServiceStateFailure is set of zinit can not spawn a service in the first place + // due to a missing executable for example. Unlike `error` which is returned if the + // service itself exits with an error. ServiceStateFailure = "failure" ) @@ -204,7 +204,6 @@ type ServiceStatus struct { func (c *Client) List() (out map[string]ServiceState, err error) { err = c.cmd("list", &out) return - } // Status returns the status of a service @@ -611,3 +610,36 @@ func (c *Client) Destroy(timeout time.Duration, services ...string) error { return nil } + +// List returns all the service monitored and their status +func (c *Client) Log(n int) (out []byte, err error) { + cmd1 := exec.Command("zinit", "log", "-s") + cmd2 := exec.Command("tail", "-n", fmt.Sprint(n)) + + cmd2.Stdin, err = cmd1.StdoutPipe() + if err != nil { + return + } + + err = cmd1.Start() + if err != nil { + return + } + + output, err := cmd2.Output() + if err != nil { + return + } + + err = cmd1.Wait() + if err != nil { + return + } + + return output, err +} + +// Restart restarts a service. +func (c *Client) Restart(service string) error { + return c.cmd(fmt.Sprintf("restart %s", service), nil) +} From af1f698cb52ce2ccae90a192c18fc1f4ebe6fb3d Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Tue, 13 May 2025 14:09:47 +0300 Subject: [PATCH 2/4] add admin interface to zos-api --- client/node.go | 79 ++++++++++++++++++++++++++++++++++-- docs/manual/api.md | 93 ++++++++++++++++++++++++++++++++++++++++--- pkg/zos_api/admin.go | 86 +++++++++++++++++++++++++++++++++++++++ pkg/zos_api/routes.go | 11 +++++ 4 files changed, 260 insertions(+), 9 deletions(-) diff --git a/client/node.go b/client/node.go index c5b70555..41cfb6c8 100644 --- a/client/node.go +++ b/client/node.go @@ -189,7 +189,7 @@ func (n *NodeClient) NetworkListInterfaces(ctx context.Context) (result map[stri // NetworkListAllInterfaces return all physical devices on a node func (n *NodeClient) NetworkListAllInterfaces(ctx context.Context) (result map[string]Interface, err error) { - const cmd = "zos.network.admin.interfaces" + const cmd = "zos.admin.interfaces" err = n.bus.Call(ctx, n.nodeTwin, cmd, nil, &result) @@ -199,19 +199,92 @@ func (n *NodeClient) NetworkListAllInterfaces(ctx context.Context) (result map[s // NetworkSetPublicExitDevice select which physical interface to use as an exit device // setting `iface` to `zos` will then make node run in a single nic setup. func (n *NodeClient) NetworkSetPublicExitDevice(ctx context.Context, iface string) error { - const cmd = "zos.network.admin.set_public_nic" + const cmd = "zos.admin.set_public_nic" return n.bus.Call(ctx, n.nodeTwin, cmd, iface, nil) } // NetworkGetPublicExitDevice gets the current dual nic setup of the node. func (n *NodeClient) NetworkGetPublicExitDevice(ctx context.Context) (exit ExitDevice, err error) { - const cmd = "zos.network.admin.get_public_nic" + const cmd = "zos.admin.get_public_nic" err = n.bus.Call(ctx, n.nodeTwin, cmd, nil, &exit) return } +// AdminRebootNode stops all the running services and reboots the node +func (n *NodeClient) AdminRebootNode(ctx context.Context) error { + const cmd = "zos.admin.reboot" + + return n.bus.Call(ctx, n.nodeTwin, cmd, nil, nil) +} + +// AdminRestartService restarts a zinit service +func (n *NodeClient) AdminRestartService(ctx context.Context, service string) error { + const cmd = "zos.admin.restart" + + return n.bus.Call(ctx, n.nodeTwin, cmd, service, nil) +} + +// AdminRestartAll restarts all zinit services +func (n *NodeClient) AdminRestartAll(ctx context.Context) error { + const cmd = "zos.admin.restart_all" + + return n.bus.Call(ctx, n.nodeTwin, cmd, nil, nil) +} + +// AdminShowLogs returns l lines of zinit logs +func (n *NodeClient) AdminShowLogs(ctx context.Context, l int) (logs []byte, err error) { + const cmd = "zos.admin.show_logs" + + err = n.bus.Call(ctx, n.nodeTwin, cmd, l, &logs) + return +} + +// AdminShowResolve return the content of /etc/resolv.conf +func (n *NodeClient) AdminShowResolve(ctx context.Context) (res []byte, err error) { + const cmd = "zos.admin.show_resolve" + + err = n.bus.Call(ctx, n.nodeTwin, cmd, nil, &res) + return +} + +// AdminShowOpenConnections return information about all open connections in the node +func (n *NodeClient) AdminShowOpenConnections(ctx context.Context) (res []byte, err error) { + const cmd = "zos.admin.show_open_connections" + + err = n.bus.Call(ctx, n.nodeTwin, cmd, nil, &res) + return +} + +// AdminStopWorkload stops a workload +func (n *NodeClient) AdminStopWorkload(ctx context.Context, twinID uint32, wlID uint64) error { + const cmd = "zos.admin.stop_workload" + args := struct { + TwinID uint32 `json:"twin_id"` + WorkloadID uint64 `json:"workload_id"` + }{ + TwinID: twinID, + WorkloadID: wlID, + } + + return n.bus.Call(ctx, n.nodeTwin, cmd, args, nil) +} + +// AdminResumeWorkload stops a workload +func (n *NodeClient) AdminResumeWorkload(ctx context.Context, twinID uint32, wlID uint64) error { + const cmd = "zos.admin.resume_workload" + args := struct { + TwinID uint32 `json:"twin_id"` + WorkloadID uint64 `json:"workload_id"` + }{ + TwinID: twinID, + WorkloadID: wlID, + } + + return n.bus.Call(ctx, n.nodeTwin, cmd, args, nil) +} + // NetworkListPublicIPs list taken public IPs on the node func (n *NodeClient) NetworkListPublicIPs(ctx context.Context) ([]string, error) { const cmd = "zos.network.list_public_ips" diff --git a/docs/manual/api.md b/docs/manual/api.md index 250eb00f..446fad35 100644 --- a/docs/manual/api.md +++ b/docs/manual/api.md @@ -151,13 +151,95 @@ it means it can act like an access node to user private networks ## Admin -The next set of commands are ONLY possible to be called by the `farmer` only. +The next set of commands are ONLY possible to be called by the `farmer` owning the node. + +### Reboot Node + +| command |body| return| +|---|---|---| +| `zos.admin.reboot` | - | - | + +Stops all services then reboots the node + +### Restart Service + +| command |body| return| +|---|---|---| +| `zos.admin.restart` | string | - | + +Restarts a service running on the node + +### Restart All Services + +| command |body| return| +|---|---|---| +| `zos.admin.restart_all` | - | - | + +Restarts all zinit services running on the node + +### Show Logs + +| command |body| return| +|---|---|---| +| `zos.admin.show_logs` | int | []byte | + +Shows a number of lines of zinit logs + +### Show Resolve + +| command |body| return| +|---|---|---| +| `zos.admin.show_resolve` | - | []byte | + +Shows the content of /etc/resolv.conf + +### Show Open Connections + +| command |body| return| +|---|---|---| +| `zos.admin.show_open_connections` | - | []byte | + +Shows information about all open connections in the node + +### Stop Workload + +| command |body| return| +|---|---|---| +| `zos.admin.Stop` | `Args` | - | + +Where + +```json +Args { + "twin_id": "uint32", + "workload_id": "uint64", +} +``` + +Stops a workload + +### Resume Workload + +| command |body| return| +|---|---|---| +| `zos.admin.resume` | `Args` | - | + +Where + +```json +Args { + "twin_id": "uint32", + "workload_id": "uint64", +} +``` + +Resumes a stopped workload ### List Physical Interfaces | command |body| return| |---|---|---| -| `zos.network.admin.interfaces` | - |`map[string]Interface` | +| `zos.admin.interfaces` | - |`map[string]Interface` | Where @@ -175,7 +257,7 @@ Those interfaces then can be used as an input to `set_public_nic` | command |body| return| |---|---|---| -| `zos.network.admin.get_public_nic` | - |`ExitDevice` | +| `zos.admin.get_public_nic` | - |`ExitDevice` | Where @@ -193,9 +275,9 @@ returns the interface used by public traffic (for user workloads) | command |body| return| |---|---|---| -| `zos.network.admin.set_public_nic` | `name` |- | +| `zos.admin.set_public_nic` | `name` |- | -name must be one of (free) names returned by `zos.network.admin.interfaces` +name must be one of (free) names returned by `zos.admin.interfaces` ## System @@ -223,7 +305,6 @@ name must be one of (free) names returned by `zos.network.admin.interfaces` |---|---|---| | `zos.system.node_features_get` | - |`[]NodeFeature` | - Where ```json diff --git a/pkg/zos_api/admin.go b/pkg/zos_api/admin.go index 4b53237e..0830323f 100644 --- a/pkg/zos_api/admin.go +++ b/pkg/zos_api/admin.go @@ -4,6 +4,11 @@ import ( "context" "encoding/json" "fmt" + "os" + "path/filepath" + + "github.com/rs/zerolog/log" + "github.com/threefoldtech/zosbase/pkg/zinit" ) func (g *ZosAPI) adminInterfacesHandler(ctx context.Context, payload []byte) (interface{}, error) { @@ -45,3 +50,84 @@ func (g *ZosAPI) adminSetPublicNICHandler(ctx context.Context, payload []byte) ( } return nil, g.networkerStub.SetPublicExitDevice(ctx, iface) } + +func (g *ZosAPI) adminRebootHandler(ctx context.Context, payload []byte) (interface{}, error) { + zinit := zinit.Default() + + return nil, zinit.Reboot() +} + +func (g *ZosAPI) adminRestartServiceHandler(ctx context.Context, payload []byte) (interface{}, error) { + var service string + if err := json.Unmarshal(payload, &service); err != nil { + return nil, fmt.Errorf("failed to decode input, expecting string: %w", err) + } + + zinit := zinit.Default() + + return nil, zinit.Restart(service) +} + +func (g *ZosAPI) adminRestartAllHandler(ctx context.Context, payload []byte) (interface{}, error) { + zinit := zinit.Default() + + services, err := zinit.List() + if err != nil { + return nil, fmt.Errorf("failed to list node services, expecting string: %w", err) + } + + for service := range services { + log.Debug().Str("service", service).Send() + if err := zinit.Restart(service); err != nil { + return nil, fmt.Errorf("failed to reboot service %s, expecting string: %w", service, err) + } + } + + return nil, nil +} + +func (g *ZosAPI) adminShowLogsHandler(ctx context.Context, payload []byte) (interface{}, error) { + var n int + if err := json.Unmarshal(payload, &n); err != nil { + return nil, fmt.Errorf("failed to decode input, expecting string: %w", err) + } + + zinit := zinit.Default() + + return zinit.Log(n) +} + +func (g *ZosAPI) adminShowResolveHandler(ctx context.Context, payload []byte) (interface{}, error) { + path := filepath.Join("/etc", "resolv.conf") + return os.ReadFile(path) +} + +func (g *ZosAPI) adminShowOpenConnectionsHandler(ctx context.Context, payload []byte) (interface{}, error) { + return g.statisticsStub.OpenConnections(ctx) +} + +func (g *ZosAPI) adminStopWorkloadHandler(ctx context.Context, payload []byte) (interface{}, error) { + var args struct { + TwinID uint32 `json:"twin_id"` + WorkloadID uint64 `json:"workload_id"` + } + + if err := json.Unmarshal(payload, &args); err != nil { + return nil, fmt.Errorf("failed to decode input, expecting twin id and workload id: %w", err) + } + + return nil, g.provisionStub.Pause(ctx, args.TwinID, args.WorkloadID) +} + +func (g *ZosAPI) adminResumeWorkloadHandler(ctx context.Context, payload []byte) (interface{}, error) { + var args struct { + TwinID uint32 `json:"twin_id"` + WorkloadID uint64 `json:"workload_id"` + } + + if err := json.Unmarshal(payload, &args); err != nil { + return nil, fmt.Errorf("failed to decode input, expecting twin id and workload id: %w", err) + } + + return nil, g.provisionStub.Resume(ctx, args.TwinID, args.WorkloadID) +} diff --git a/pkg/zos_api/routes.go b/pkg/zos_api/routes.go index cde39c78..a0b20278 100644 --- a/pkg/zos_api/routes.go +++ b/pkg/zos_api/routes.go @@ -49,6 +49,17 @@ func (g *ZosAPI) SetupRoutes(router *peer.Router) { admin.WithHandler("set_public_nic", g.adminSetPublicNICHandler) admin.WithHandler("get_public_nic", g.adminGetPublicNICHandler) + admin.WithHandler("reboot", g.adminRebootHandler) + admin.WithHandler("restart", g.adminRestartServiceHandler) + admin.WithHandler("restart_all", g.adminRestartAllHandler) + + admin.WithHandler("show_logs", g.adminShowLogsHandler) + admin.WithHandler("show_resolve", g.adminShowResolveHandler) + admin.WithHandler("show_open_connections", g.adminShowOpenConnectionsHandler) + + admin.WithHandler("stop_workload", g.adminStopWorkloadHandler) + admin.WithHandler("resume_workload", g.adminResumeWorkloadHandler) + location := root.SubRoute("location") location.WithHandler("get", g.locationGet) From 4ee07830a36347a142c1911af7be63d394991bfc Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Tue, 13 May 2025 14:48:23 +0300 Subject: [PATCH 3/4] add admin interface to zos-api-light --- docs/manual/api.md | 4 +- pkg/zos_api_light/admin.go | 85 +++++++++++++++++++++++++++++++++++++ pkg/zos_api_light/routes.go | 11 +++++ 3 files changed, 98 insertions(+), 2 deletions(-) diff --git a/docs/manual/api.md b/docs/manual/api.md index 446fad35..a7056400 100644 --- a/docs/manual/api.md +++ b/docs/manual/api.md @@ -210,7 +210,7 @@ Shows information about all open connections in the node Where ```json -Args { +args { "twin_id": "uint32", "workload_id": "uint64", } @@ -227,7 +227,7 @@ Stops a workload Where ```json -Args { +args { "twin_id": "uint32", "workload_id": "uint64", } diff --git a/pkg/zos_api_light/admin.go b/pkg/zos_api_light/admin.go index 81d1292b..265d70a5 100644 --- a/pkg/zos_api_light/admin.go +++ b/pkg/zos_api_light/admin.go @@ -4,6 +4,11 @@ import ( "context" "encoding/json" "fmt" + "os" + "path/filepath" + + "github.com/rs/zerolog/log" + "github.com/threefoldtech/zosbase/pkg/zinit" ) func (g *ZosAPI) adminInterfacesHandler(ctx context.Context, payload []byte) (interface{}, error) { @@ -44,5 +49,85 @@ func (g *ZosAPI) adminSetPublicNICHandler(ctx context.Context, payload []byte) ( return nil, fmt.Errorf("failed to decode input, expecting string: %w", err) } return nil, fmt.Errorf("not supported") +} + +func (g *ZosAPI) adminRebootHandler(ctx context.Context, payload []byte) (interface{}, error) { + zinit := zinit.Default() + + return nil, zinit.Reboot() +} + +func (g *ZosAPI) adminRestartServiceHandler(ctx context.Context, payload []byte) (interface{}, error) { + var service string + if err := json.Unmarshal(payload, &service); err != nil { + return nil, fmt.Errorf("failed to decode input, expecting string: %w", err) + } + + zinit := zinit.Default() + + return nil, zinit.Restart(service) +} + +func (g *ZosAPI) adminRestartAllHandler(ctx context.Context, payload []byte) (interface{}, error) { + zinit := zinit.Default() + + services, err := zinit.List() + if err != nil { + return nil, fmt.Errorf("failed to list node services, expecting string: %w", err) + } + + for service := range services { + log.Debug().Str("service", service).Send() + if err := zinit.Restart(service); err != nil { + return nil, fmt.Errorf("failed to reboot service %s, expecting string: %w", service, err) + } + } + + return nil, nil +} + +func (g *ZosAPI) adminShowLogsHandler(ctx context.Context, payload []byte) (interface{}, error) { + var n int + if err := json.Unmarshal(payload, &n); err != nil { + return nil, fmt.Errorf("failed to decode input, expecting string: %w", err) + } + + zinit := zinit.Default() + + return zinit.Log(n) +} + +func (g *ZosAPI) adminShowResolveHandler(ctx context.Context, payload []byte) (interface{}, error) { + path := filepath.Join("/etc", "resolv.conf") + return os.ReadFile(path) +} + +func (g *ZosAPI) adminShowOpenConnectionsHandler(ctx context.Context, payload []byte) (interface{}, error) { + return g.statisticsStub.OpenConnections(ctx) +} + +func (g *ZosAPI) adminStopWorkloadHandler(ctx context.Context, payload []byte) (interface{}, error) { + var args struct { + TwinID uint32 `json:"twin_id"` + WorkloadID uint64 `json:"workload_id"` + } + + if err := json.Unmarshal(payload, &args); err != nil { + return nil, fmt.Errorf("failed to decode input, expecting twin id and workload id: %w", err) + } + + return nil, g.provisionStub.Pause(ctx, args.TwinID, args.WorkloadID) +} + +func (g *ZosAPI) adminResumeWorkloadHandler(ctx context.Context, payload []byte) (interface{}, error) { + var args struct { + TwinID uint32 `json:"twin_id"` + WorkloadID uint64 `json:"workload_id"` + } + + if err := json.Unmarshal(payload, &args); err != nil { + return nil, fmt.Errorf("failed to decode input, expecting twin id and workload id: %w", err) + } + return nil, g.provisionStub.Resume(ctx, args.TwinID, args.WorkloadID) } diff --git a/pkg/zos_api_light/routes.go b/pkg/zos_api_light/routes.go index 755de6f9..ebfd12c1 100644 --- a/pkg/zos_api_light/routes.go +++ b/pkg/zos_api_light/routes.go @@ -49,6 +49,17 @@ func (g *ZosAPI) SetupRoutes(router *peer.Router) { admin.WithHandler("set_public_nic", g.adminSetPublicNICHandler) admin.WithHandler("get_public_nic", g.adminGetPublicNICHandler) + admin.WithHandler("reboot", g.adminRebootHandler) + admin.WithHandler("restart", g.adminRestartServiceHandler) + admin.WithHandler("restart_all", g.adminRestartAllHandler) + + admin.WithHandler("show_logs", g.adminShowLogsHandler) + admin.WithHandler("show_resolve", g.adminShowResolveHandler) + admin.WithHandler("show_open_connections", g.adminShowOpenConnectionsHandler) + + admin.WithHandler("stop_workload", g.adminStopWorkloadHandler) + admin.WithHandler("resume_workload", g.adminResumeWorkloadHandler) + location := root.SubRoute("location") location.WithHandler("get", g.locationGet) } From 6771c93751cb5edafc0416526f55ba170f4a910e Mon Sep 17 00:00:00 2001 From: Eslam-Nawara Date: Tue, 27 May 2025 13:35:41 +0300 Subject: [PATCH 4/4] fix restart service command --- pkg/zinit/commands.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/zinit/commands.go b/pkg/zinit/commands.go index 8adb7543..be96f862 100644 --- a/pkg/zinit/commands.go +++ b/pkg/zinit/commands.go @@ -641,5 +641,5 @@ func (c *Client) Log(n int) (out []byte, err error) { // Restart restarts a service. func (c *Client) Restart(service string) error { - return c.cmd(fmt.Sprintf("restart %s", service), nil) + return exec.Command("zinit", "restart", service).Run() }