sdk/python3/api.go (98 lines of code) (raw):

// Copyright 2017 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main /* #cgo pkg-config: python3 #define Py_LIMITED_API #include <Python.h> #include "api.h" // Go cannot call C variadic functions. This simple wrapper allows us to call ParseTuple and expect // two strings. static int PyArg_ParseTuple_ss(PyObject *args, char **a, char **b) { return PyArg_ParseTuple(args, "ss", a, b); } // Go cannot use C macros directly. Py_RETURN_NONE is the standard mechanism for returning the // PyNone object after incrementing its ref count. static PyObject* none() { Py_RETURN_NONE; } */ import "C" import ( "sync" "unsafe" "github.com/GoogleCloudPlatform/ubbagent/sdk" ) // We store all current agents in a map keyed by an incrementing integer. Since the Python side of // our module can't hold onto a Go reference, it instead holds onto the agent number which is // subsequently used to retrieve the actual agent object when performing operations. var agentCount C.int = 0 var agents = make(map[C.int]*sdk.Agent) // A mutex that protects the agents map against concurrent modification. var agentsmu = sync.RWMutex{} //export AgentInit func AgentInit(self *C.Agent, args *C.PyObject, _ *C.PyObject) C.int { var cConfigData *C.char var cStateDir *C.char if C.PyArg_ParseTuple_ss(args, &cConfigData, &cStateDir) == 0 { return -1 } agentsmu.Lock() defer agentsmu.Unlock() num := agentCount agentCount++ goConfigData := []byte(C.GoString(cConfigData)) goStateDir := C.GoString(cStateDir) agent, err := sdk.NewAgent(goConfigData, goStateDir) if err != nil { setException(err.Error()) return -1 } agents[num] = agent self.agentnum = num return 0 } //export AgentShutdown func AgentShutdown(self *C.Agent, _ *C.PyObject) *C.PyObject { agentsmu.Lock() defer agentsmu.Unlock() agent, exists := agents[self.agentnum] if !exists { setException("Agent already shutdown") return nil } delete(agents, self.agentnum) err := agent.Shutdown() if err != nil { setException(err.Error()) return nil } return C.none() } //export AgentDealloc func AgentDealloc(self *C.Agent) { agentsmu.Lock() defer agentsmu.Unlock() agent, exists := agents[self.agentnum] if !exists { return } delete(agents, self.agentnum) // Ignore any shutdown errors agent.Shutdown() } //export AgentAddReport func AgentAddReport(self *C.Agent, report *C.PyObject) *C.PyObject { var reportStr *C.PyObject = C.PyUnicode_AsEncodedString(report, C.CString("UTF-8"), C.CString("strict")) var reportData *C.char = C.PyBytes_AsString(reportStr) C.Py_DecRef(reportStr) agentsmu.RLock() defer agentsmu.RUnlock() goReportData := []byte(C.GoString(reportData)) agent, exists := agents[self.agentnum] if !exists { setException("Agent already shutdown") return nil } if err := agent.AddReportJson(goReportData); err != nil { setException(err.Error()) return nil } return C.none() } //export AgentGetStatus func AgentGetStatus(self *C.Agent, _ *C.PyObject) *C.PyObject { agentsmu.RLock() defer agentsmu.RUnlock() agent, exists := agents[self.agentnum] if !exists { setException("Agent already shutdown") return nil } marshaled, err := agent.GetStatusJson() if err != nil { setException(err.Error()) return nil } status := C.CString(string(marshaled)) defer C.free(unsafe.Pointer(status)) return C.PyUnicode_FromString(status) } func setException(err string) { errCStr := C.CString(err) defer C.free(unsafe.Pointer(errCStr)) C.PyErr_SetString(C.AgentError, errCStr) } // Required empty func func main() {}