diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index fef32d376..edeba829b 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -25,7 +25,7 @@ RUN sudo apt-get -qq install -y --no-install-recommends \ curl # Assuming installing to /usr/local -ENV LD_LIBRARY_PATH=/usr/local/lib +ENV LD_LIBRARY_PATH=/usr/lib:/usr/local/lib RUN curl -s -L https://github.com/Kitware/CMake/releases/download/v3.26.4/cmake-3.26.4-linux-$(uname -m).sh > cmake.sh ;\ sudo bash cmake.sh --prefix=/usr/local --skip-license ;\ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 0f0daae1d..7752a791a 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -7,7 +7,10 @@ "vscode": { "settings": { "terminal.integrated.defaultProfile.linux": "bash" - } + }, + "extensions": [ + "ms-vscode.cmake-tools" + ] } } }, diff --git a/.gitignore b/.gitignore index 4f66700fa..cff963592 100644 --- a/.gitignore +++ b/.gitignore @@ -131,3 +131,6 @@ compile_flags.txt # Rules to ignore auto-generated test harness scripts test_*.t !/src/bindings/python/test_commands/test_runner.t + +# Go +resource/reapi/bindings/go/src/test/main diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c299b010..ccb6a1ad7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,6 +174,12 @@ if(ENABLE_COVERAGE) SET( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${COVERAGE_FLAGS}" ) endif() +# export WITH_GO=yes +if(DEFINED ENV{WITH_GO}) + message(STATUS "WITH_GO detected in main CMakeLists.txt to build go bindings") + include(GolangSimple) +endif() + include_directories(.) add_subdirectory( etc ) add_subdirectory( src ) diff --git a/README.md b/README.md index 72737f09f..072892759 100644 --- a/README.md +++ b/README.md @@ -104,13 +104,37 @@ be set to the same prefix as was used to install the target flux-core. For example, if flux-core was installed in `$FLUX_CORE_PREFIX`: -``` +```bash ./configure --prefix=${FLUX_CORE_PREFIX} make make check make install ``` +To build go bindings, you will need go (tested with 1.19.10) available, and then: + +```bash +export WITH_GO=yes +./configure +make +``` + +To run just one test, you can cd into t + +```bash +$ ./t9001-golang-basic.t +ok 1 - match allocate 1 slot: 1 socket: 1 core (pol=default) +ok 2 - match allocate 2 slots: 2 sockets: 5 cores 1 gpu 6 memory +# passed all 2 test(s) +1..2 +``` + +To run full tests (more robust and mimics what happens in CI) you can do: + +```bash +make check +``` + ##### Flux Instance The examples below walk through exercising functioning flux-sched modules (i.e., diff --git a/cmake/GolangSimple.cmake b/cmake/GolangSimple.cmake new file mode 100644 index 000000000..f4b30d9b4 --- /dev/null +++ b/cmake/GolangSimple.cmake @@ -0,0 +1,44 @@ + +set(CUSTOM_GO_PATH "${CMAKE_SOURCE_DIR}/resource/reapi/bindings/go") +set(GOPATH "${CMAKE_CURRENT_BINARY_DIR}/go") + +# This probably isn't necessary, although if we could build fluxcli into it maybe +file(MAKE_DIRECTORY ${GOPATH}) + +# GO_GET will retrieve a go module that needs to be installed +function(GO_GET TARG) + add_custom_target(${TARG} env GOPATH=${GOPATH} go get ${ARGN}) +endfunction(GO_GET) + +# ADD_GO_INSTALLABLE_PROGRAM builds a custom go program (primarily for testing) +function(ADD_GO_INSTALLABLE_PROGRAM NAME MAIN_SRC CGO_CFLAGS CGO_LIBRARY_FLAGS) + message(STATUS "GOPATH: ${GOPATH}") + message(STATUS "CGO_LDFLAGS (before): ${CGO_LIBRARY_FLAGS}") + message(STATUS "TEST_FLAGS: ${TEST_FLAGS}") + get_filename_component(MAIN_SRC_ABS ${MAIN_SRC} ABSOLUTE) + add_custom_target(${NAME}) + + # string(REPLACE ...) + STRING(REPLACE ";" " " CGO_LDFLAGS "${CGO_LIBRARY_FLAGS}") + + # set(ENV{} []) as environment OR without CMake variable + # Note that I couldn't get this to work (the spaces are always escaped) so I hard coded for now + # We need a solution that takes the CGO_LIBRARY_FLAGS arg, and can pass (with spaces not escaped) to add_custom_command + SET ($ENV{CGO_LDFLAGS} "${CGO_LDFLAGS}") + message(STATUS "CGO_LDFLAGS (after): ${CGO_LDFLAGS}") + # SET (CMAKE_GO_FLAGS "${CGO_LDFLAGS}") + + add_custom_command(TARGET ${NAME} + COMMAND GOPATH=${GOPATH}:${CUSTOM_GO_PATH} GOOS=linux G0111MODULE=off CGO_CFLAGS="${CGO_CFLAGS}" CGO_LDFLAGS='-L${CMAKE_BINARY_DIR}/resource/reapi/bindings -L/workspaces/fs-dev/resource/libjobspec -ljobspec_conv -lreapi_cli -L${CMAKE_BINARY_DIR}/resource -lresource -lflux-idset -lstdc++ -lczmq -ljansson -lhwloc -lboost_system -lflux-hostlist -lboost_graph -lyaml-cpp' go build -ldflags '-w' + -o "${CMAKE_CURRENT_SOURCE_DIR}/${NAME}" + ${CMAKE_GO_FLAGS} ${MAIN_SRC} + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + DEPENDS ${MAIN_SRC_ABS} + COMMENT "Building Go library") + foreach(DEP ${ARGN}) + add_dependencies(${NAME} ${DEP}) + endforeach() + + add_custom_target(${NAME}_all ALL DEPENDS ${NAME}) + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${NAME} DESTINATION bin) +endfunction(ADD_GO_INSTALLABLE_PROGRAM) \ No newline at end of file diff --git a/configure.ac b/configure.ac index b2b83a970..dbd244210 100644 --- a/configure.ac +++ b/configure.ac @@ -28,6 +28,14 @@ AC_PROG_LN_S AC_PROG_MAKE_SET AM_PROG_CC_C_O AX_CODE_COVERAGE +# Check to build with Go +AC_PROG_GO([go]) +AC_PATH_PROG(GO, go, nogo) +AS_IF([test "$ac_cv_path_GO" = "nogo"],AC_MSG_NOTICE([could not find go]),[]) +AM_CONDITIONAL([WITH_GO], [test "X$WITH_GO" != "X"]) +if test "$WITH_GO" = yes; then + GO_PRINTOUT=$( echo "GO...............: $(go version | sed 's/go version //')" ) +fi if test "$GCC" = yes; then WARNING_CFLAGS="-Wall -Werror -Werror=missing-field-initializers -Wno-error=deprecated-declarations" @@ -202,6 +210,9 @@ AC_CONFIG_FILES([Makefile resource/reapi/Makefile resource/reapi/bindings/Makefile resource/reapi/bindings/c/Makefile + resource/reapi/bindings/go/Makefile + resource/reapi/bindings/go/src/Makefile + resource/reapi/bindings/go/src/test/Makefile resource/policies/Makefile resource/policies/base/Makefile resource/policies/base/test/Makefile @@ -231,4 +242,5 @@ echo " LDFLAGS..........: $LDFLAGS LIBS.............: $LIBS Linker...........: $LD + $GO_PRINTOUT " diff --git a/resource/reapi/bindings/CMakeLists.txt b/resource/reapi/bindings/CMakeLists.txt index c478213aa..30ed443db 100644 --- a/resource/reapi/bindings/CMakeLists.txt +++ b/resource/reapi/bindings/CMakeLists.txt @@ -18,3 +18,8 @@ add_library ( reapi_module STATIC target_link_libraries(reapi_module PRIVATE flux::core ) + +if(DEFINED ENV{WITH_GO}) + message(STATUS "WITH_GO is set to build go bindings") + add_subdirectory( go ) +endif() \ No newline at end of file diff --git a/resource/reapi/bindings/Makefile.am b/resource/reapi/bindings/Makefile.am index 6f20b92e0..c0cdfbd7a 100644 --- a/resource/reapi/bindings/Makefile.am +++ b/resource/reapi/bindings/Makefile.am @@ -1 +1 @@ -SUBDIRS = c +SUBDIRS = c go diff --git a/resource/reapi/bindings/c/reapi_module.cpp b/resource/reapi/bindings/c/reapi_module.cpp index 9456251ed..1c42a6ea6 100644 --- a/resource/reapi/bindings/c/reapi_module.cpp +++ b/resource/reapi/bindings/c/reapi_module.cpp @@ -37,7 +37,7 @@ extern "C" reapi_module_ctx_t *reapi_module_new () errno = ENOMEM; goto out; } - ctx->h = NULL; + ctx->h = flux_open (NULL, 0); out: return ctx; } diff --git a/resource/reapi/bindings/go/CMakeLists.txt b/resource/reapi/bindings/go/CMakeLists.txt new file mode 100644 index 000000000..39838feed --- /dev/null +++ b/resource/reapi/bindings/go/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory( src ) \ No newline at end of file diff --git a/resource/reapi/bindings/go/Makefile.am b/resource/reapi/bindings/go/Makefile.am new file mode 100644 index 000000000..af437a64d --- /dev/null +++ b/resource/reapi/bindings/go/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = src diff --git a/resource/reapi/bindings/go/go.mod b/resource/reapi/bindings/go/go.mod new file mode 100644 index 000000000..659f614b3 --- /dev/null +++ b/resource/reapi/bindings/go/go.mod @@ -0,0 +1,3 @@ +module github.com/flux-framework/flux-sched/resource/reapi/bindings/go + +go 1.19 \ No newline at end of file diff --git a/resource/reapi/bindings/go/src/CMakeLists.txt b/resource/reapi/bindings/go/src/CMakeLists.txt new file mode 100644 index 000000000..4f50cacf2 --- /dev/null +++ b/resource/reapi/bindings/go/src/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory( test ) \ No newline at end of file diff --git a/resource/reapi/bindings/go/src/Makefile.am b/resource/reapi/bindings/go/src/Makefile.am new file mode 100644 index 000000000..02af5b3e3 --- /dev/null +++ b/resource/reapi/bindings/go/src/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = test diff --git a/resource/reapi/bindings/go/src/fluxcli/go.mod b/resource/reapi/bindings/go/src/fluxcli/go.mod new file mode 100644 index 000000000..f4bd84a3a --- /dev/null +++ b/resource/reapi/bindings/go/src/fluxcli/go.mod @@ -0,0 +1,3 @@ +module github.com/flux-framework/flux-sched/resource/reapi/bindings/go/src/fluxcli + +go 1.19 diff --git a/resource/reapi/bindings/go/src/fluxcli/reapi_cli.go b/resource/reapi/bindings/go/src/fluxcli/reapi_cli.go new file mode 100644 index 000000000..9409f8e71 --- /dev/null +++ b/resource/reapi/bindings/go/src/fluxcli/reapi_cli.go @@ -0,0 +1,234 @@ +/*****************************************************************************\ + * Copyright 2023 Lawrence Livermore National Security, LLC + * (c.f. AUTHORS, NOTICE.LLNS, LICENSE) + * + * This file is part of the Flux resource manager framework. + * For details, see https://github.com/flux-framework. + * + * SPDX-License-Identifier: LGPL-3.0 +\*****************************************************************************/ + +package fluxcli + +/* +#include "reapi_cli.h" +*/ +import "C" +import ( + "fmt" +) + +type ( + ReapiCtx C.struct_reapi_cli_ctx_t + + // ReapiClient is a flux resource API client + // it holds a context that is required for most interactinos + ReapiClient struct { + ctx *ReapiCtx + } +) + +// NewReapiCli creates a new resource API client +// reapi_cli_ctx_t *reapi_cli_new (); +func NewReapiClient() *ReapiClient { + ctx := (*ReapiCtx)(C.reapi_cli_new()) + return &ReapiClient{ctx: ctx} +} + +// Given an integer return code, convert to go error +// Also provide a meaningful string to the developer user +func retvalToError(code int, message string) error { + if code == 0 { + return nil + } + return fmt.Errorf(message+" %d", code) +} + +// HasContext exposes the private ctx, telling the caller if it is set +func (cli *ReapiClient) HasContext() bool { + return cli.ctx != nil +} + +// Destroy destroys a resource API context +// void reapi_cli_destroy (reapi_cli_ctx_t *ctx); +func (cli *ReapiClient) Destroy() { + C.reapi_cli_destroy((*C.struct_reapi_cli_ctx)(cli.ctx)) +} + +// InitContext initializes Fluxion with resource graph +// \param rgraph string encoding the resource graph +// \param options json string initialization options +// int reapi_cli_initialize (reapi_cli_ctx_t *ctx, const char *jgf); +func (cli *ReapiClient) InitContext(jgf string, options string) (err error) { + + jobgraph := C.CString(jgf) + opts := C.CString(options) + fluxerr := (int)( + C.reapi_cli_initialize( + (*C.struct_reapi_cli_ctx)(cli.ctx), jobgraph, (opts), + ), + ) + + return retvalToError(fluxerr, "issue initializing resource api client") +} + +// MatchAllocate matches a jobspec to the "best" resources and either allocate +// +// orelse reserve them. The best resources are determined by +// the selected match policy. +// +// \param orelse_reserve +// Boolean: if false, only allocate; otherwise, first try +// to allocate and if that fails, reserve. +// \param jobspec jobspec string. +// \param jobid jobid of the uint64_t type. +// \param reserved Boolean into which to return true if this job has been +// reserved instead of allocated. +// \param R String into which to return the resource set either +// allocated or reserved. +// \param at If allocated, 0 is returned; if reserved, actual time +// at which the job is reserved. +// \param ov Double into which to return performance overhead +// in terms of elapse time needed to complete +// the match operation. +// \return 0 on success; -1 on error. +func (cli *ReapiClient) MatchAllocate( + orelse_reserve bool, + jobspec string, +) (reserved bool, allocated string, at int64, overhead float64, jobid uint64, err error) { + var r = C.CString("") + spec := C.CString(jobspec) + + fluxerr := (int)(C.reapi_cli_match_allocate((*C.struct_reapi_cli_ctx)(cli.ctx), + (C.bool)(orelse_reserve), + spec, + (*C.ulong)(&jobid), + (*C.bool)(&reserved), + &r, + (*C.long)(&at), + (*C.double)(&overhead))) + + allocated = C.GoString(r) + + err = retvalToError(fluxerr, "issue resource api client matching allocate") + return reserved, allocated, at, overhead, jobid, err + +} + +// UpdateAllocate updates the resource state with R. +// +// \param jobid jobid of the uint64_t type. +// \param R R string +// \param at return the scheduled time +// \param ov return the performance overhead +// in terms of elapse time needed to complete +// the match operation. +// \param R_out return the updated R string. +// \return 0 on success; -1 on error. +// +// int reapi_cli_update_allocate (reapi_cli_ctx_t *ctx, +// +// const uint64_t jobid, const char *R, int64_t *at, +// double *ov, const char **R_out); +func (cli *ReapiClient) UpdateAllocate(jobid int, r string) (at int64, overhead float64, r_out string, err error) { + var tmp_rout = C.CString("") + var resource = C.CString(r) + + fluxerr := (int)(C.reapi_cli_update_allocate((*C.struct_reapi_cli_ctx)(cli.ctx), + (C.ulong)(jobid), + resource, + (*C.long)(&at), + (*C.double)(&overhead), + &tmp_rout)) + + r_out = C.GoString(tmp_rout) + + err = retvalToError(fluxerr, "issue resource api client updating allocate") + return at, overhead, r_out, err +} + +// Cancel cancels the allocation or reservation corresponding to jobid. +// +// \param jobid jobid of the uint64_t type. +// \param noent_ok don't return an error on nonexistent jobid +// \return 0 on success; -1 on error. +// +// int reapi_cli_cancel (reapi_cli_ctx_t *ctx, +// +// const uint64_t jobid, bool noent_ok); +func (cli *ReapiClient) Cancel(jobid int64, noent_ok bool) (err error) { + fluxerr := (int)(C.reapi_cli_cancel((*C.struct_reapi_cli_ctx)(cli.ctx), + (C.ulong)(jobid), + (C.bool)(noent_ok))) + return retvalToError(fluxerr, "issue resource api client cancel") +} + +// Info gets the information on the allocation or reservation corresponding to jobid +// +// \param jobid const jobid of the uint64_t type. +// \param reserved Boolean into which to return true if this job has been +// reserved instead of allocated. +// \param at If allocated, 0 is returned; if reserved, actual time +// at which the job is reserved. +// \param ov Double into which to return performance overhead +// in terms of elapse time needed to complete +// the match operation. +// \return 0 on success; -1 on error. +// +// int reapi_cli_info (reapi_cli_ctx_t *ctx, const uint64_t jobid, +// +// bool *reserved, int64_t *at, double *ov); +func (cli *ReapiClient) Info(jobid int64) (reserved bool, at int64, overhead float64, mode string, err error) { + var tmp_mode = C.CString("") + + fluxerr := (int)(C.reapi_cli_info((*C.struct_reapi_cli_ctx)(cli.ctx), + (C.ulong)(jobid), + (&tmp_mode), + (*C.bool)(&reserved), + (*C.long)(&at), + (*C.double)(&overhead))) + + err = retvalToError(fluxerr, "issue resource api client info") + return reserved, at, overhead, C.GoString(tmp_mode), err +} + +// Stat gets the performance information about the resource infrastructure. +// +// \param V Number of resource vertices +// \param E Number of edges +// \param J Number of jobs +// \param load Graph load time +// \param min Min match time +// \param max Max match time +// \param avg Avg match time +// \return 0 on success; -1 on error. +// +// int reapi_cli_stat (reapi_cli_ctx_t *ctx, int64_t *V, int64_t *E, +// +// int64_t *J, double *load, +// double *min, double *max, double *avg); +func (cli *ReapiClient) Stat() (v int64, e int64, + jobs int64, load float64, min float64, max float64, avg float64, err error) { + fluxerr := (int)(C.reapi_cli_stat((*C.struct_reapi_cli_ctx)(cli.ctx), + (*C.long)(&v), + (*C.long)(&e), + (*C.long)(&jobs), + (*C.double)(&load), + (*C.double)(&min), + (*C.double)(&max), + (*C.double)(&avg))) + + err = retvalToError(fluxerr, "issue resource api client stat") + return v, e, jobs, load, min, max, avg, err +} + +// GetErrMsg returns a string error message from the resource api +func (cli *ReapiClient) GetErrMsg() string { + errmsg := C.reapi_cli_get_err_msg((*C.struct_reapi_cli_ctx)(cli.ctx)) + return C.GoString(errmsg) +} + +// ClearErrMsg clears error messages +func (cli *ReapiClient) ClearErrMsg() { + C.reapi_cli_clear_err_msg((*C.struct_reapi_cli_ctx)(cli.ctx)) +} diff --git a/resource/reapi/bindings/go/src/fluxcli/reapi_cli_test.go b/resource/reapi/bindings/go/src/fluxcli/reapi_cli_test.go new file mode 100644 index 000000000..7bf8fa3c6 --- /dev/null +++ b/resource/reapi/bindings/go/src/fluxcli/reapi_cli_test.go @@ -0,0 +1,21 @@ +/*****************************************************************************\ + * Copyright 2023 Lawrence Livermore National Security, LLC + * (c.f. AUTHORS, NOTICE.LLNS, LICENSE) + * + * This file is part of the Flux resource manager framework. + * For details, see https://github.com/flux-framework. + * + * SPDX-License-Identifier: LGPL-3.0 +\*****************************************************************************/ + +package fluxcli + +import "testing" + +func TestFluxcli(t *testing.T) { + cli := NewReapiClient() + if !cli.HasContext() { + t.Errorf("Context is null") + } + +} diff --git a/resource/reapi/bindings/go/src/fluxmodule/reapi_module.go b/resource/reapi/bindings/go/src/fluxmodule/reapi_module.go new file mode 100644 index 000000000..668ae2a4e --- /dev/null +++ b/resource/reapi/bindings/go/src/fluxmodule/reapi_module.go @@ -0,0 +1,126 @@ +/*****************************************************************************\ + * Copyright 2023 Lawrence Livermore National Security, LLC + * (c.f. AUTHORS, NOTICE.LLNS, LICENSE) + * + * This file is part of the Flux resource manager framework. + * For details, see https://github.com/flux-framework. + * + * SPDX-License-Identifier: LGPL-3.0 +\*****************************************************************************/ + +package fluxcli + +// #include "reapi_module.h" +import "C" +import ( + "fmt" + "unsafe" +) + +type ( + ReapiCtx C.struct_reapi_module_ctx_t + ReapiModule struct { + ctx ReapiCtx + } +) + +// NewReapiModule creates a new resource API module +// reapi_module_ctx_t *reapi_module_new (); +func NewReapiModule() *ReapiModule { + ctx := (*ReapiCtx)(C.reapi_module_new()) + return &ReapiModule{ctx: ctx} +} + +// HasContext exposes the private ctx, telling the caller if it is set +func (m *ReapiModule) HasContext() bool { + return m.ctx != nil +} + +// Given an integer return code, convert to go error +// Also provide a meaningful string to the developer user +func retvalToError(code int, message string) error { + if code == 0 { + return nil + } + return fmt.Errorf(message+" %d", code) +} + +// Destroy destroys the resource API context +// void reapi_module_destroy (reapi_module_ctx_t *ctx); +func (m *ReapiModule) Destroy() { + C.reapi_module_destroy((*C.struct_reapi_module_ctx)(m.ctx)) +} + +// MatchAllocate matches and allocates resources +// int reapi_module_match_allocate (reapi_module_ctx_t *ctx, bool orelse_reserve, +// at: is the scheduled time "at" +func (m *ReapiModule) MatchAllocate( + orelse_reserve bool, + jobspec string, + jobid int, +) (reserved bool, allocated string, at int64, overhead float64, err error) { + // var atlong C.long = (C.long)(at) + var r = C.CString("teststring") + + // Jobspec as a CString + spec := C.CString(jobspec) + + fluxerr := (int)(C.reapi_module_match_allocate((*C.struct_reapi_module_ctx)(m.ctx), + (C.bool)(orelse_reserve), + spec, + (C.ulong)(jobid), + (*C.bool)(&reserved), + &r, + (*C.long)(&at), + (*C.double)(&overhead))) + + allocated = C.GoString(r) + + err = retvalToError(fluxerr, "issue matching allocation for resource api") + return reserved, allocated, at, overhead, err +} + +// Info gets the information on the allocation or reservation corresponding +// to jobid. +// +// \param ctx reapi_module_ctx_t context object +// \param jobid const jobid of the uint64_t type. +// \param reserved Boolean into which to return true if this job has been +// reserved instead of allocated. +// \param at If allocated, 0 is returned; if reserved, actual time +// at which the job is reserved. +// \param ov Double into which to return performance overhead +// in terms of elapse time needed to complete +// the match operation. +// \return 0 on success; -1 on error. +// +// int reapi_module_info (reapi_module_ctx_t *ctx, const uint64_t jobid, +// +// bool *reserved, int64_t *at, double *ov); +func (m *ReapiModule) Info(ctx *ReapiCtx, jobid int64) (reserved bool, at int64, overhead float64, err error) { + fluxerr := (int)(C.reapi_module_info((*C.struct_reapi_module_ctx)(m.ctx), + (C.ulong)(jobid), + (*C.bool)(&reserved), + (*C.long)(&at), + (*C.double)(&overhead))) + + err = retvalToError(fluxerr, "issue getting module info") + return reserved, at, overhead, err +} + +// Cancel cancels the allocation or reservation corresponding to jobid. +// +// \param ctx reapi_module_ctx_t context object +// \param jobid jobid of the uint64_t type. +// \param noent_ok don't return an error on nonexistent jobid +// \return 0 on success; -1 on error. +// +// int reapi_module_cancel (reapi_module_ctx_t *ctx, +// +// const uint64_t jobid, bool noent_ok);*/ +func (m *ReapiModule) Cancel(jobid int64, noent_ok bool) (err error) { + fluxerr := (int)(C.reapi_module_cancel((*C.struct_reapi_module_ctx)(m.ctx), + (C.ulong)(jobid), + (C.bool)(noent_ok))) + return retvalToError(fluxerr, "issue with cancel") +} \ No newline at end of file diff --git a/resource/reapi/bindings/go/src/fluxmodule/reapi_module_test.go b/resource/reapi/bindings/go/src/fluxmodule/reapi_module_test.go new file mode 100644 index 000000000..7177d7fd1 --- /dev/null +++ b/resource/reapi/bindings/go/src/fluxmodule/reapi_module_test.go @@ -0,0 +1,28 @@ +/*****************************************************************************\ + * Copyright 2023 Lawrence Livermore National Security, LLC + * (c.f. AUTHORS, NOTICE.LLNS, LICENSE) + * + * This file is part of the Flux resource manager framework. + * For details, see https://github.com/flux-framework. + * + * SPDX-License-Identifier: LGPL-3.0 +\*****************************************************************************/ + +package fluxmodule + +import ( + "fmt" + "os" + "testing" +) + +func TestFluxmodule(t *testing.T) { + mod := NewReapiModule() + jobspec, err := os.ReadFile("/root/flux-sched/t/data/resource/jobspecs/basics/test001.yaml") + if !mod.HasContext() { + t.Errorf("Context is null") + } + reserved, allocated, at, overhead, err1 := mod.MatchAllocate(false, string(jobspec), 4) + fmt.Printf("%t, %s, %d, %f, %d, %s", reserved, allocated, at, overhead, err1, err) + +} diff --git a/resource/reapi/bindings/go/src/test/CMakeLists.txt b/resource/reapi/bindings/go/src/test/CMakeLists.txt new file mode 100644 index 000000000..ed718f027 --- /dev/null +++ b/resource/reapi/bindings/go/src/test/CMakeLists.txt @@ -0,0 +1,14 @@ +set(TARGET main) +set(SRCS main.go) +set(CGO_CFLAGS "-I${CMAKE_BINARY_DIR}/resource/reapi/bindings/c") +set(CGO_LIBRARY_FLAGS "-L${CMAKE_BINARY_DIR}/resource/reapi/bindings;-lreapi_cli;-L${CMAKE_BINARY_DIR}/resource;-lresource;-lflux-idset;-lstdc++;-lczmq;-ljansson;-lhwloc;-lboost_system;-lflux-hostlist;-lboost_graph;-lyaml-cpp") + +# This ensures the main binary is cleaned up +set_directory_properties( + PROPERTIES + ADDITIONAL_CLEAN_FILES "${CMAKE_CURRENT_BINARY_DIR}/main" + ) + +# GO_GET(go_redis github.com/hoisie/redis) +# main main.go +ADD_GO_INSTALLABLE_PROGRAM(${TARGET} ${SRCS} "${CGO_CFLAGS}" "${CGO_LIBRARY_FLAGS}" reapi_module reapi_cli flux::core) diff --git a/resource/reapi/bindings/go/src/test/Makefile.am b/resource/reapi/bindings/go/src/test/Makefile.am new file mode 100644 index 000000000..cbfa9e8be --- /dev/null +++ b/resource/reapi/bindings/go/src/test/Makefile.am @@ -0,0 +1,43 @@ +AUTOMAKE_OPTIONS = subdir-objects no-dependencies + +AM_CFLAGS = \ + -I$(abs_top_builddir)/resource/reapi/bindings/c + +GOPATH = \ + $(abs_top_builddir)/resource/reapi/bindings/go + +GO111MODULE = off + +AM_LDFLAGS = \ + -L$(top_builddir)/resource/reapi/bindings/c/.libs \ + -lreapi_cli \ + -L$(top_builddir)/resource/.libs \ + -lresource \ + -lflux-idset \ + -lstdc++ \ + -lczmq \ + -ljansson \ + -lhwloc \ + -lboost_system \ + -lflux-hostlist \ + -lboost_graph \ + -lyaml-cpp + +COMMONENVVAR=GOOS=$(shell uname -s | tr A-Z a-z) + +if WITH_GO + TESTS = main$(EXEEXT) + check_PROGRAMS = $(TESTS) +endif + +main_SOURCES = main.go + +main$(EXEEXT): + $(TOP_DIR) GOPATH=$(GOPATH) $(COMMONENVVAR) GO111MODULE=$(GO111MODULE) CGO_CFLAGS="$(AM_CFLAGS)" CGO_LDFLAGS="$(AM_LDFLAGS)" $(GOC) build -ldflags '-w' -o $@ $< + +clean: + rm -f main main.log main.trs + +clean-local: + -rm -f main main.log main.trs + diff --git a/resource/reapi/bindings/go/src/test/main.go b/resource/reapi/bindings/go/src/test/main.go new file mode 100644 index 000000000..797984475 --- /dev/null +++ b/resource/reapi/bindings/go/src/test/main.go @@ -0,0 +1,87 @@ +/*****************************************************************************\ + * Copyright 2023 Lawrence Livermore National Security, LLC + * (c.f. AUTHORS, NOTICE.LLNS, LICENSE) + * + * This file is part of the Flux resource manager framework. + * For details, see https://github.com/flux-framework. + * + * SPDX-License-Identifier: LGPL-3.0 +\*****************************************************************************/ + +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/flux-framework/flux-sched/resource/reapi/bindings/go/src/fluxcli" +) + +func main() { + jgfPtr := flag.String("jgf", "", "path to jgf") + jobspecPtr := flag.String("jobspec", "", "path to jobspec") + reserve := flag.Bool("reserve", false, "or else reserve?") + flag.Parse() + + jgf, err := os.ReadFile(*jgfPtr) + if err != nil { + fmt.Println("Error reading JGF file") + return + } + cli := fluxcli.NewReapiClient() + err = cli.InitContext(string(jgf), "{}") + if err != nil { + fmt.Printf("Error initializing jobspec context for ReapiClient: %v\n", err) + return + } + fmt.Printf("Errors so far: %s\n", cli.GetErrMsg()) + + jobspec, err := os.ReadFile(*jobspecPtr) + if err != nil { + fmt.Printf("Error reading jobspec file: %v\n", err) + return + } + fmt.Printf("Jobspec:\n %s\n", jobspec) + + reserved, allocated, at, overhead, jobid, err := cli.MatchAllocate(*reserve, string(jobspec)) + if err != nil { + fmt.Printf("Error in ReapiClient MatchAllocate: %v\n", err) + return + } + printOutput(reserved, allocated, at, jobid, err) + reserved, allocated, at, overhead, jobid, err = cli.MatchAllocate(*reserve, string(jobspec)) + fmt.Println("Errors so far: \n", cli.GetErrMsg()) + + if err != nil { + fmt.Printf("Error in ReapiClient MatchAllocate: %v\n", err) + return + } + printOutput(reserved, allocated, at, jobid, err) + err = cli.Cancel(1, false) + if err != nil { + fmt.Printf("Error in ReapiClient Cancel: %v\n", err) + return + } + fmt.Printf("Cancel output: %v\n", err) + + reserved, at, overhead, mode, err := cli.Info(1) + if err != nil { + fmt.Printf("Error in ReapiClient Info: %v\n", err) + return + } + fmt.Printf("Info output jobid 1: %t, %d, %f, %s, %v\n", reserved, at, overhead, mode, err) + + reserved, at, overhead, mode, err = cli.Info(2) + if err != nil { + fmt.Println("Error in ReapiClient Info: %v\n", err) + return + } + fmt.Printf("Info output jobid 2: %t, %d, %f, %v\n", reserved, at, overhead, err) + +} + +func printOutput(reserved bool, allocated string, at int64, jobid uint64, err error) { + fmt.Println("\n\t----Match Allocate output---") + fmt.Printf("jobid: %d\nreserved: %t\nallocated: %s\nat: %d\nerror: %v\n", jobid, reserved, allocated, at, err) +} diff --git a/src/test/docker/docker-run-checks.sh b/src/test/docker/docker-run-checks.sh index fc9587917..4215dea1b 100755 --- a/src/test/docker/docker-run-checks.sh +++ b/src/test/docker/docker-run-checks.sh @@ -217,7 +217,7 @@ else -e chain_lint \ -e JOBS \ -e USER \ - -e PROJECT \ + -e PROJECT \ -e CI \ -e TAP_DRIVER_QUIET \ -e FLUX_TEST_TIMEOUT \ @@ -232,6 +232,7 @@ else -e S3_SECRET_ACCESS_KEY \ -e S3_HOSTNAME \ -e S3_BUCKET \ + -e WITH_GO \ --cap-add SYS_PTRACE \ --tty \ ${INTERACTIVE:+--interactive} \ diff --git a/src/test/docker/focal-golang/Dockerfile b/src/test/docker/focal-golang/Dockerfile new file mode 100644 index 000000000..9c0340a28 --- /dev/null +++ b/src/test/docker/focal-golang/Dockerfile @@ -0,0 +1,42 @@ +FROM fluxrm/flux-core:focal + +ARG USER=flux +ARG UID=1000 + +# Install extra buildrequires for flux-sched: +RUN sudo apt-get update +RUN sudo apt-get -qq install -y --no-install-recommends \ + libboost-graph-dev \ + libboost-system-dev \ + libboost-filesystem-dev \ + libboost-regex-dev \ + python-yaml \ + libyaml-cpp-dev \ + ninja-build \ + curl \ + libedit-dev + +# Install cmake for new build system +RUN curl -s -L https://github.com/Kitware/CMake/releases/download/v3.26.4/cmake-3.26.4-linux-$(uname -m).sh > cmake.sh ;\ + sudo bash cmake.sh --prefix=/usr/local --skip-license ;\ + rm cmake.sh + +# Install Golang 1.19.10 for binding tests +RUN wget https://go.dev/dl/go1.19.10.linux-amd64.tar.gz +RUN sudo rm -rf /usr/local/go \ + && sudo tar -C /usr/local -xzf go1.19.10.linux-amd64.tar.gz \ + && rm -f go1.19.10.linux-amd64.tar.gz + +# Add configured user to image with sudo access: +# +RUN \ + if test "$USER" != "flux"; then \ + sudo groupadd -g $UID $USER \ + && sudo useradd -g $USER -u $UID -d /home/$USER -m $USER \ + && sudo sh -c "printf \"$USER ALL= NOPASSWD: ALL\\n\" >> /etc/sudoers" \ + && sudo adduser $USER sudo ; \ + fi + +USER $USER +WORKDIR /home/$USER +ENV PATH=$PATH:/usr/local/go/bin \ No newline at end of file diff --git a/src/test/generate-matrix.py b/src/test/generate-matrix.py index dcfa98372..3b6131de8 100755 --- a/src/test/generate-matrix.py +++ b/src/test/generate-matrix.py @@ -187,6 +187,15 @@ def __str__(self): docker_tag=True, ) +# Ubuntu 20.04: py3.8 +matrix.add_build( + name="focal - golang-test", + image="focal-golang", + env=dict( + WITH_GO="yes", + ), +) + # RHEL8 clone matrix.add_build( name="el8", diff --git a/t/CMakeLists.txt b/t/CMakeLists.txt index dbb8559f2..42c9e4ad5 100644 --- a/t/CMakeLists.txt +++ b/t/CMakeLists.txt @@ -1,6 +1,12 @@ # NOTE: skipping tree tests, since they're long broken # t2000-tree-basic.t # t2001-tree-real.t + +set(GO_TESTS "") +if(DEFINED ENV{WITH_GO}) + set(GO_TESTS "t9001-golang-basic.t") +endif() + set(ALL_TESTS t0000-sharness.t t1001-qmanager-basic.t @@ -81,6 +87,7 @@ set(ALL_TESTS t6002-graph-hwloc.t t7000-shell-datastaging.t t8001-util-ion-R.t + ${GO_TESTS} ) foreach(test ${ALL_TESTS}) flux_add_test(NAME ${test} diff --git a/t/Makefile.am b/t/Makefile.am index 41489f57e..566b7260f 100644 --- a/t/Makefile.am +++ b/t/Makefile.am @@ -100,7 +100,8 @@ TESTS = \ t6001-match-formats.t \ t6002-graph-hwloc.t \ t7000-shell-datastaging.t \ - t8001-util-ion-R.t + t8001-util-ion-R.t \ + t9001-golang-basic.t check_SCRIPTS = $(TESTS) diff --git a/t/data/resource/expected/golang/001.R.out b/t/data/resource/expected/golang/001.R.out new file mode 100644 index 000000000..7a9f056cf --- /dev/null +++ b/t/data/resource/expected/golang/001.R.out @@ -0,0 +1,54 @@ +Errors so far: +Jobspec: + version: 9999 +resources: + - type: cluster + count: 1 + with: + - type: rack + count: 1 + with: + - type: node + count: 1 + with: + - type: slot + count: 1 + label: default + with: + - type: socket + count: 1 + with: + - type: core + count: 1 +# a comment +attributes: + system: + duration: 3600 +tasks: + - command: [ "app" ] + slot: default + count: + per_slot: 1 + + + + ----Match Allocate output--- +jobid: 1 +reserved: false +allocated: {"graph": {"nodes": [{"id": "79", "metadata": {"type": "core", "basename": "core", "name": "core35", "id": 35, "uniq_id": 79, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket1/core35"}}}, {"id": "7", "metadata": {"type": "socket", "basename": "socket", "name": "socket1", "id": 1, "uniq_id": 7, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket1"}}}, {"id": "3", "metadata": {"type": "node", "basename": "node", "name": "node1", "id": 1, "uniq_id": 3, "rank": -1, "exclusive": false, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1"}}}, {"id": "1", "metadata": {"type": "rack", "basename": "rack", "name": "rack0", "id": 0, "uniq_id": 1, "rank": -1, "exclusive": false, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0"}}}, {"id": "0", "metadata": {"type": "cluster", "basename": "tiny", "name": "tiny0", "id": 0, "uniq_id": 0, "rank": -1, "exclusive": false, "unit": "", "size": 1, "paths": {"containment": "/tiny0"}}}], "edges": [{"source": "7", "target": "79", "metadata": {"name": {"containment": "contains"}}}, {"source": "3", "target": "7", "metadata": {"name": {"containment": "contains"}}}, {"source": "1", "target": "3", "metadata": {"name": {"containment": "contains"}}}, {"source": "0", "target": "1", "metadata": {"name": {"containment": "contains"}}}]}} + +at: 0 +error: +Errors so far: + + + ----Match Allocate output--- +jobid: 2 +reserved: false +allocated: {"graph": {"nodes": [{"id": "43", "metadata": {"type": "core", "basename": "core", "name": "core35", "id": 35, "uniq_id": 43, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket1/core35"}}}, {"id": "5", "metadata": {"type": "socket", "basename": "socket", "name": "socket1", "id": 1, "uniq_id": 5, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket1"}}}, {"id": "2", "metadata": {"type": "node", "basename": "node", "name": "node0", "id": 0, "uniq_id": 2, "rank": -1, "exclusive": false, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0"}}}, {"id": "1", "metadata": {"type": "rack", "basename": "rack", "name": "rack0", "id": 0, "uniq_id": 1, "rank": -1, "exclusive": false, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0"}}}, {"id": "0", "metadata": {"type": "cluster", "basename": "tiny", "name": "tiny0", "id": 0, "uniq_id": 0, "rank": -1, "exclusive": false, "unit": "", "size": 1, "paths": {"containment": "/tiny0"}}}], "edges": [{"source": "5", "target": "43", "metadata": {"name": {"containment": "contains"}}}, {"source": "2", "target": "5", "metadata": {"name": {"containment": "contains"}}}, {"source": "1", "target": "2", "metadata": {"name": {"containment": "contains"}}}, {"source": "0", "target": "1", "metadata": {"name": {"containment": "contains"}}}]}} + +at: 0 +error: +Cancel output: +Info output jobid 1: false, 0, 0.000000, CANCELED, +Info output jobid 2: false, 0, 0.000000, diff --git a/t/data/resource/expected/golang/002.R.out b/t/data/resource/expected/golang/002.R.out new file mode 100644 index 000000000..7917bce3d --- /dev/null +++ b/t/data/resource/expected/golang/002.R.out @@ -0,0 +1,51 @@ +Errors so far: +Jobspec: + version: 9999 +resources: + - type: slot + count: 2 + label: default + with: + - type: node + count: 1 + with: + - type: socket + count: 2 + with: + - type: core + count: 5 + - type: gpu + count: 1 + - type: memory + count: 6 + +# a comment +attributes: + system: + duration: 3600 +tasks: + - command: [ "app" ] + slot: default + count: + per_slot: 1 + + + ----Match Allocate output--- +jobid: 1 +reserved: false +allocated: {"graph": {"nodes": [{"id": "21", "metadata": {"type": "core", "basename": "core", "name": "core13", "id": 13, "uniq_id": 21, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket0/core13"}}}, {"id": "22", "metadata": {"type": "core", "basename": "core", "name": "core14", "id": 14, "uniq_id": 22, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket0/core14"}}}, {"id": "23", "metadata": {"type": "core", "basename": "core", "name": "core15", "id": 15, "uniq_id": 23, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket0/core15"}}}, {"id": "24", "metadata": {"type": "core", "basename": "core", "name": "core16", "id": 16, "uniq_id": 24, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket0/core16"}}}, {"id": "25", "metadata": {"type": "core", "basename": "core", "name": "core17", "id": 17, "uniq_id": 25, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket0/core17"}}}, {"id": "80", "metadata": {"type": "gpu", "basename": "gpu", "name": "gpu0", "id": 0, "uniq_id": 80, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket0/gpu0"}}}, {"id": "85", "metadata": {"type": "memory", "basename": "memory", "name": "memory1", "id": 1, "uniq_id": 85, "rank": -1, "exclusive": true, "unit": "", "size": 2, "paths": {"containment": "/tiny0/rack0/node0/socket0/memory1"}}}, {"id": "86", "metadata": {"type": "memory", "basename": "memory", "name": "memory2", "id": 2, "uniq_id": 86, "rank": -1, "exclusive": true, "unit": "", "size": 2, "paths": {"containment": "/tiny0/rack0/node0/socket0/memory2"}}}, {"id": "87", "metadata": {"type": "memory", "basename": "memory", "name": "memory3", "id": 3, "uniq_id": 87, "rank": -1, "exclusive": true, "unit": "", "size": 2, "paths": {"containment": "/tiny0/rack0/node0/socket0/memory3"}}}, {"id": "4", "metadata": {"type": "socket", "basename": "socket", "name": "socket0", "id": 0, "uniq_id": 4, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket0"}}}, {"id": "39", "metadata": {"type": "core", "basename": "core", "name": "core31", "id": 31, "uniq_id": 39, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket1/core31"}}}, {"id": "40", "metadata": {"type": "core", "basename": "core", "name": "core32", "id": 32, "uniq_id": 40, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket1/core32"}}}, {"id": "41", "metadata": {"type": "core", "basename": "core", "name": "core33", "id": 33, "uniq_id": 41, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket1/core33"}}}, {"id": "42", "metadata": {"type": "core", "basename": "core", "name": "core34", "id": 34, "uniq_id": 42, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket1/core34"}}}, {"id": "43", "metadata": {"type": "core", "basename": "core", "name": "core35", "id": 35, "uniq_id": 43, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket1/core35"}}}, {"id": "81", "metadata": {"type": "gpu", "basename": "gpu", "name": "gpu1", "id": 1, "uniq_id": 81, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket1/gpu1"}}}, {"id": "89", "metadata": {"type": "memory", "basename": "memory", "name": "memory5", "id": 5, "uniq_id": 89, "rank": -1, "exclusive": true, "unit": "", "size": 2, "paths": {"containment": "/tiny0/rack0/node0/socket1/memory5"}}}, {"id": "90", "metadata": {"type": "memory", "basename": "memory", "name": "memory6", "id": 6, "uniq_id": 90, "rank": -1, "exclusive": true, "unit": "", "size": 2, "paths": {"containment": "/tiny0/rack0/node0/socket1/memory6"}}}, {"id": "91", "metadata": {"type": "memory", "basename": "memory", "name": "memory7", "id": 7, "uniq_id": 91, "rank": -1, "exclusive": true, "unit": "", "size": 2, "paths": {"containment": "/tiny0/rack0/node0/socket1/memory7"}}}, {"id": "5", "metadata": {"type": "socket", "basename": "socket", "name": "socket1", "id": 1, "uniq_id": 5, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0/socket1"}}}, {"id": "2", "metadata": {"type": "node", "basename": "node", "name": "node0", "id": 0, "uniq_id": 2, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node0"}}}, {"id": "57", "metadata": {"type": "core", "basename": "core", "name": "core13", "id": 13, "uniq_id": 57, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket0/core13"}}}, {"id": "58", "metadata": {"type": "core", "basename": "core", "name": "core14", "id": 14, "uniq_id": 58, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket0/core14"}}}, {"id": "59", "metadata": {"type": "core", "basename": "core", "name": "core15", "id": 15, "uniq_id": 59, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket0/core15"}}}, {"id": "60", "metadata": {"type": "core", "basename": "core", "name": "core16", "id": 16, "uniq_id": 60, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket0/core16"}}}, {"id": "61", "metadata": {"type": "core", "basename": "core", "name": "core17", "id": 17, "uniq_id": 61, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket0/core17"}}}, {"id": "82", "metadata": {"type": "gpu", "basename": "gpu", "name": "gpu0", "id": 0, "uniq_id": 82, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket0/gpu0"}}}, {"id": "93", "metadata": {"type": "memory", "basename": "memory", "name": "memory1", "id": 1, "uniq_id": 93, "rank": -1, "exclusive": true, "unit": "", "size": 2, "paths": {"containment": "/tiny0/rack0/node1/socket0/memory1"}}}, {"id": "94", "metadata": {"type": "memory", "basename": "memory", "name": "memory2", "id": 2, "uniq_id": 94, "rank": -1, "exclusive": true, "unit": "", "size": 2, "paths": {"containment": "/tiny0/rack0/node1/socket0/memory2"}}}, {"id": "95", "metadata": {"type": "memory", "basename": "memory", "name": "memory3", "id": 3, "uniq_id": 95, "rank": -1, "exclusive": true, "unit": "", "size": 2, "paths": {"containment": "/tiny0/rack0/node1/socket0/memory3"}}}, {"id": "6", "metadata": {"type": "socket", "basename": "socket", "name": "socket0", "id": 0, "uniq_id": 6, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket0"}}}, {"id": "75", "metadata": {"type": "core", "basename": "core", "name": "core31", "id": 31, "uniq_id": 75, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket1/core31"}}}, {"id": "76", "metadata": {"type": "core", "basename": "core", "name": "core32", "id": 32, "uniq_id": 76, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket1/core32"}}}, {"id": "77", "metadata": {"type": "core", "basename": "core", "name": "core33", "id": 33, "uniq_id": 77, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket1/core33"}}}, {"id": "78", "metadata": {"type": "core", "basename": "core", "name": "core34", "id": 34, "uniq_id": 78, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket1/core34"}}}, {"id": "79", "metadata": {"type": "core", "basename": "core", "name": "core35", "id": 35, "uniq_id": 79, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket1/core35"}}}, {"id": "83", "metadata": {"type": "gpu", "basename": "gpu", "name": "gpu1", "id": 1, "uniq_id": 83, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket1/gpu1"}}}, {"id": "97", "metadata": {"type": "memory", "basename": "memory", "name": "memory5", "id": 5, "uniq_id": 97, "rank": -1, "exclusive": true, "unit": "", "size": 2, "paths": {"containment": "/tiny0/rack0/node1/socket1/memory5"}}}, {"id": "98", "metadata": {"type": "memory", "basename": "memory", "name": "memory6", "id": 6, "uniq_id": 98, "rank": -1, "exclusive": true, "unit": "", "size": 2, "paths": {"containment": "/tiny0/rack0/node1/socket1/memory6"}}}, {"id": "99", "metadata": {"type": "memory", "basename": "memory", "name": "memory7", "id": 7, "uniq_id": 99, "rank": -1, "exclusive": true, "unit": "", "size": 2, "paths": {"containment": "/tiny0/rack0/node1/socket1/memory7"}}}, {"id": "7", "metadata": {"type": "socket", "basename": "socket", "name": "socket1", "id": 1, "uniq_id": 7, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1/socket1"}}}, {"id": "3", "metadata": {"type": "node", "basename": "node", "name": "node1", "id": 1, "uniq_id": 3, "rank": -1, "exclusive": true, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0/node1"}}}, {"id": "1", "metadata": {"type": "rack", "basename": "rack", "name": "rack0", "id": 0, "uniq_id": 1, "rank": -1, "exclusive": false, "unit": "", "size": 1, "paths": {"containment": "/tiny0/rack0"}}}, {"id": "0", "metadata": {"type": "cluster", "basename": "tiny", "name": "tiny0", "id": 0, "uniq_id": 0, "rank": -1, "exclusive": false, "unit": "", "size": 1, "paths": {"containment": "/tiny0"}}}], "edges": [{"source": "4", "target": "21", "metadata": {"name": {"containment": "contains"}}}, {"source": "4", "target": "22", "metadata": {"name": {"containment": "contains"}}}, {"source": "4", "target": "23", "metadata": {"name": {"containment": "contains"}}}, {"source": "4", "target": "24", "metadata": {"name": {"containment": "contains"}}}, {"source": "4", "target": "25", "metadata": {"name": {"containment": "contains"}}}, {"source": "4", "target": "80", "metadata": {"name": {"containment": "contains"}}}, {"source": "4", "target": "85", "metadata": {"name": {"containment": "contains"}}}, {"source": "4", "target": "86", "metadata": {"name": {"containment": "contains"}}}, {"source": "4", "target": "87", "metadata": {"name": {"containment": "contains"}}}, {"source": "2", "target": "4", "metadata": {"name": {"containment": "contains"}}}, {"source": "5", "target": "39", "metadata": {"name": {"containment": "contains"}}}, {"source": "5", "target": "40", "metadata": {"name": {"containment": "contains"}}}, {"source": "5", "target": "41", "metadata": {"name": {"containment": "contains"}}}, {"source": "5", "target": "42", "metadata": {"name": {"containment": "contains"}}}, {"source": "5", "target": "43", "metadata": {"name": {"containment": "contains"}}}, {"source": "5", "target": "81", "metadata": {"name": {"containment": "contains"}}}, {"source": "5", "target": "89", "metadata": {"name": {"containment": "contains"}}}, {"source": "5", "target": "90", "metadata": {"name": {"containment": "contains"}}}, {"source": "5", "target": "91", "metadata": {"name": {"containment": "contains"}}}, {"source": "2", "target": "5", "metadata": {"name": {"containment": "contains"}}}, {"source": "1", "target": "2", "metadata": {"name": {"containment": "contains"}}}, {"source": "6", "target": "57", "metadata": {"name": {"containment": "contains"}}}, {"source": "6", "target": "58", "metadata": {"name": {"containment": "contains"}}}, {"source": "6", "target": "59", "metadata": {"name": {"containment": "contains"}}}, {"source": "6", "target": "60", "metadata": {"name": {"containment": "contains"}}}, {"source": "6", "target": "61", "metadata": {"name": {"containment": "contains"}}}, {"source": "6", "target": "82", "metadata": {"name": {"containment": "contains"}}}, {"source": "6", "target": "93", "metadata": {"name": {"containment": "contains"}}}, {"source": "6", "target": "94", "metadata": {"name": {"containment": "contains"}}}, {"source": "6", "target": "95", "metadata": {"name": {"containment": "contains"}}}, {"source": "3", "target": "6", "metadata": {"name": {"containment": "contains"}}}, {"source": "7", "target": "75", "metadata": {"name": {"containment": "contains"}}}, {"source": "7", "target": "76", "metadata": {"name": {"containment": "contains"}}}, {"source": "7", "target": "77", "metadata": {"name": {"containment": "contains"}}}, {"source": "7", "target": "78", "metadata": {"name": {"containment": "contains"}}}, {"source": "7", "target": "79", "metadata": {"name": {"containment": "contains"}}}, {"source": "7", "target": "83", "metadata": {"name": {"containment": "contains"}}}, {"source": "7", "target": "97", "metadata": {"name": {"containment": "contains"}}}, {"source": "7", "target": "98", "metadata": {"name": {"containment": "contains"}}}, {"source": "7", "target": "99", "metadata": {"name": {"containment": "contains"}}}, {"source": "3", "target": "7", "metadata": {"name": {"containment": "contains"}}}, {"source": "1", "target": "3", "metadata": {"name": {"containment": "contains"}}}, {"source": "0", "target": "1", "metadata": {"name": {"containment": "contains"}}}]}} + +at: 0 +error: +Errors so far: + + + ----Match Allocate output--- +jobid: 2 +reserved: false +allocated: +at: 0 +error: +Cancel output: +Info output jobid 1: false, 0, 0.000000, CANCELED, +Info output jobid 2: false, 0, 0.000000, diff --git a/t/t9001-golang-basic.t b/t/t9001-golang-basic.t new file mode 100755 index 000000000..7695f646f --- /dev/null +++ b/t/t9001-golang-basic.t @@ -0,0 +1,35 @@ +#!/bin/sh + +test_description='Test Golang Bindings On Tiny Machine Configuration' + +. $(dirname $0)/sharness.sh + +if test -z "$WITH_GO"; then + skip_all='skipping Golang tests since WITH_GO not set' + test_done +fi + +if ! which go >/dev/null; then + skip_all='skipping Golang tests since no go executable found' + test_done +fi + +exp_dir="${SHARNESS_TEST_SRCDIR}/data/resource/expected/golang" +jgf="${SHARNESS_TEST_SRCDIR}/data/resource/jgfs/tiny.json" +jobspec1="${SHARNESS_TEST_SRCDIR}/data/resource/jobspecs/basics/test001.yaml" +jobspec2="${SHARNESS_TEST_SRCDIR}/data/resource/jobspecs/basics/test003.yaml" +main="../../resource/reapi/bindings/go/src/test/main" + +test001_desc="match allocate 1 slot: 1 socket: 1 core (pol=default)" +test_expect_success "${test001_desc}" ' + ${main} --jgf=${jgf} --jobspec=${jobspec1} > 001.R.out && + test_cmp 001.R.out ${exp_dir}/001.R.out +' + +test002_desc="match allocate 2 slots: 2 sockets: 5 cores 1 gpu 6 memory" +test_expect_success "${test002_desc}" ' + ${main} --jgf=${jgf} --jobspec=${jobspec2} > 002.R.out && + test_cmp 002.R.out ${exp_dir}/002.R.out +' + +test_done