From 9d47b6843f3fb91c2fcab4514ea10f23ea2e9e6c Mon Sep 17 00:00:00 2001 From: James Shubin Date: Fri, 1 Dec 2023 15:18:12 -0500 Subject: [PATCH] engine, gapi, lang, lib: Plumb through new local API This is a new API that is similar in spirit and plumbing to the World API, but it intended for all local machine operations and will likely only ever have one implementation. --- engine/graph/engine.go | 3 +++ engine/graph/state.go | 3 +++ engine/local/local.go | 36 ++++++++++++++++++++++++++++++++++++ engine/resources.go | 6 ++++++ gapi/gapi.go | 2 ++ lang/funcs/dage/dage.go | 3 +++ lang/gapi/gapi.go | 3 +++ lang/interfaces/func.go | 3 +++ lang/interpret_test.go | 22 ++++++++++++++++++++++ lang/lang.go | 4 ++++ lib/main.go | 12 ++++++++++++ 11 files changed, 97 insertions(+) create mode 100644 engine/local/local.go diff --git a/engine/graph/engine.go b/engine/graph/engine.go index 91b29992..5faa5d2e 100644 --- a/engine/graph/engine.go +++ b/engine/graph/engine.go @@ -28,6 +28,7 @@ import ( "github.com/purpleidea/mgmt/converger" "github.com/purpleidea/mgmt/engine" + "github.com/purpleidea/mgmt/engine/local" engineUtil "github.com/purpleidea/mgmt/engine/util" "github.com/purpleidea/mgmt/pgraph" "github.com/purpleidea/mgmt/util/errwrap" @@ -47,6 +48,7 @@ type Engine struct { Hostname string Converger *converger.Coordinator + Local *local.API World engine.World // Prefix is a unique directory prefix which can be used. It should be @@ -224,6 +226,7 @@ func (obj *Engine) Commit() error { Hostname: obj.Hostname, //Converger: obj.Converger, + Local: obj.Local, World: obj.World, Prefix: statePrefix, diff --git a/engine/graph/state.go b/engine/graph/state.go index cd23c67e..33cbff10 100644 --- a/engine/graph/state.go +++ b/engine/graph/state.go @@ -25,6 +25,7 @@ import ( "github.com/purpleidea/mgmt/converger" "github.com/purpleidea/mgmt/engine" + "github.com/purpleidea/mgmt/engine/local" engineUtil "github.com/purpleidea/mgmt/engine/util" "github.com/purpleidea/mgmt/pgraph" "github.com/purpleidea/mgmt/util/errwrap" @@ -46,6 +47,7 @@ type State struct { //Converger *converger.Coordinator + Local *local.API World engine.World // Prefix is a unique directory prefix which can be used. It should be @@ -239,6 +241,7 @@ func (obj *State) Init() error { return graph, nil // we return in a func so it's fresh! }, + Local: obj.Local, World: obj.World, VarDir: obj.varDir, diff --git a/engine/local/local.go b/engine/local/local.go new file mode 100644 index 00000000..84e9acf1 --- /dev/null +++ b/engine/local/local.go @@ -0,0 +1,36 @@ +// Mgmt +// Copyright (C) 2013-2023+ James Shubin and the project contributors +// Written by James Shubin and the project contributors +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// Package local contains functions and interfaces that are shared between +// functions and resources. It's similar to the "world" functionality, except +// that it only involves local operations that stay within a single machine or +// local mgmt instance. +package local + +import ( + "context" +) + +// API implements the base handle for all the methods in this package. If we +// were going to have more than one implementation for all of these, then this +// would be an interface instead, and different packages would implement it. +// Since this is not the expectation for the local API, it's all self-contained. +type API struct { + Prefix string + Debug bool + Logf func(format string, v ...interface{}) +} diff --git a/engine/resources.go b/engine/resources.go index 2e41f188..c163d769 100644 --- a/engine/resources.go +++ b/engine/resources.go @@ -22,6 +22,7 @@ import ( "encoding/gob" "fmt" + "github.com/purpleidea/mgmt/engine/local" "github.com/purpleidea/mgmt/pgraph" "github.com/purpleidea/mgmt/util/errwrap" @@ -135,6 +136,11 @@ type Init struct { // TODO: GraphQuery offers an interface to query the resource graph. + // Local has a bunch of methods and properties which are useful for + // operations on the local machine and for communication between + // functions and resources. + Local *local.API + // World provides a connection to the outside world. This is most often // used for communicating with the distributed database. World World diff --git a/gapi/gapi.go b/gapi/gapi.go index c10d6ff7..b67f4cc8 100644 --- a/gapi/gapi.go +++ b/gapi/gapi.go @@ -23,6 +23,7 @@ import ( "fmt" "github.com/purpleidea/mgmt/engine" + "github.com/purpleidea/mgmt/engine/local" "github.com/purpleidea/mgmt/pgraph" "github.com/urfave/cli/v2" @@ -72,6 +73,7 @@ type Data struct { Program string // name of the originating program Version string // version of the originating program Hostname string // uuid for the host, required for GAPI + Local *local.API World engine.World Noop bool NoStreamWatch bool diff --git a/lang/funcs/dage/dage.go b/lang/funcs/dage/dage.go index 08c6efa0..df231a1b 100644 --- a/lang/funcs/dage/dage.go +++ b/lang/funcs/dage/dage.go @@ -29,6 +29,7 @@ import ( "time" "github.com/purpleidea/mgmt/engine" + "github.com/purpleidea/mgmt/engine/local" "github.com/purpleidea/mgmt/lang/funcs/structs" "github.com/purpleidea/mgmt/lang/interfaces" "github.com/purpleidea/mgmt/lang/types" @@ -44,6 +45,7 @@ type Engine struct { Name string Hostname string + Local *local.API World engine.World Debug bool @@ -289,6 +291,7 @@ func (obj *Engine) addVertex(f interfaces.Func) error { Input: node.input, Output: node.output, Txn: node.txn, + Local: obj.Local, World: obj.World, Debug: obj.Debug, Logf: func(format string, v ...interface{}) { diff --git a/lang/gapi/gapi.go b/lang/gapi/gapi.go index 1b975158..40205da2 100644 --- a/lang/gapi/gapi.go +++ b/lang/gapi/gapi.go @@ -259,6 +259,7 @@ func (obj *GAPI) Cli(cliInfo *gapi.CliInfo) (*gapi.Deploy, error) { LexParser: parser.LexParse, Downloader: downloader, StrInterpolater: interpolate.StrInterpolate, + //Local: obj.Local, // TODO: do we need this? //World: obj.World, // TODO: do we need this? Prefix: prefix, @@ -467,6 +468,7 @@ func (obj *GAPI) LangInit() error { Input: input, Hostname: obj.data.Hostname, + Local: obj.data.Local, World: obj.data.World, Debug: obj.data.Debug, Logf: func(format string, v ...interface{}) { @@ -747,6 +749,7 @@ func (obj *GAPI) Get(getInfo *gapi.GetInfo) error { LexParser: parser.LexParse, Downloader: downloader, StrInterpolater: interpolate.StrInterpolate, + //Local: obj.Local, // TODO: do we need this? //World: obj.World, // TODO: do we need this? Prefix: prefix, diff --git a/lang/interfaces/func.go b/lang/interfaces/func.go index 63b3662f..f0ec2a6b 100644 --- a/lang/interfaces/func.go +++ b/lang/interfaces/func.go @@ -23,6 +23,7 @@ import ( "strings" "github.com/purpleidea/mgmt/engine" + "github.com/purpleidea/mgmt/engine/local" "github.com/purpleidea/mgmt/lang/types" "github.com/purpleidea/mgmt/pgraph" ) @@ -61,6 +62,8 @@ type Init struct { Txn Txn // TODO: should we pass in a *Scope here for functions like template() ? + + Local *local.API World engine.World Debug bool diff --git a/lang/interpret_test.go b/lang/interpret_test.go index c60c2e27..32c3a196 100644 --- a/lang/interpret_test.go +++ b/lang/interpret_test.go @@ -36,6 +36,7 @@ import ( "github.com/purpleidea/mgmt/engine" "github.com/purpleidea/mgmt/engine/graph" "github.com/purpleidea/mgmt/engine/graph/autoedge" + "github.com/purpleidea/mgmt/engine/local" engineUtil "github.com/purpleidea/mgmt/engine/util" "github.com/purpleidea/mgmt/etcd" "github.com/purpleidea/mgmt/lang/ast" @@ -717,6 +718,15 @@ func TestAstFunc2(t *testing.T) { afs := &afero.Afero{Fs: mmFs} // wrap so that we're implementing ioutil fs := &util.Fs{Afero: afs} + // implementation of the Local API (we only expect just this single one) + localAPI := &local.API{ + Prefix: fmt.Sprintf("%s/", filepath.Join(tmpdir, "local")), + Debug: testing.Verbose(), // set via the -test.v flag to `go test` + Logf: func(format string, v ...interface{}) { + logf("local: api: "+format, v...) + }, + } + // implementation of the World API (alternatives can be substituted in) world := &etcd.World{ //Hostname: hostname, @@ -1011,6 +1021,7 @@ func TestAstFunc2(t *testing.T) { funcs := &dage.Engine{ Name: "test", Hostname: "", // NOTE: empty b/c not used + Local: localAPI, // used partially in some tests World: world, // used partially in some tests //Prefix: fmt.Sprintf("%s/", filepath.Join(tmpdir, "funcs")), Debug: testing.Verbose(), // set via the -test.v flag to `go test` @@ -1479,6 +1490,15 @@ func TestAstFunc3(t *testing.T) { afs := &afero.Afero{Fs: mmFs} // wrap so that we're implementing ioutil fs := &util.Fs{Afero: afs} + // implementation of the Local API (we only expect just this single one) + localAPI := &local.API{ + Prefix: fmt.Sprintf("%s/", filepath.Join(tmpdir, "local")), + Debug: testing.Verbose(), // set via the -test.v flag to `go test` + Logf: func(format string, v ...interface{}) { + logf("local: api: "+format, v...) + }, + } + // implementation of the World API (alternatives can be substituted in) world := &etcd.World{ //Hostname: hostname, @@ -1773,6 +1793,7 @@ func TestAstFunc3(t *testing.T) { funcs := &dage.Engine{ Name: "test", Hostname: "", // NOTE: empty b/c not used + Local: localAPI, // used partially in some tests World: world, // used partially in some tests //Prefix: fmt.Sprintf("%s/", filepath.Join(tmpdir, "funcs")), Debug: testing.Verbose(), // set via the -test.v flag to `go test` @@ -2004,6 +2025,7 @@ func TestAstFunc3(t *testing.T) { //Version: obj.Version, Hostname: "localhost", Converger: converger, + Local: localAPI, World: world, Prefix: fmt.Sprintf("%s/", filepath.Join(tmpdir, "engine")), Debug: testing.Verbose(), diff --git a/lang/lang.go b/lang/lang.go index 3d8f4bd7..eebe0b87 100644 --- a/lang/lang.go +++ b/lang/lang.go @@ -26,6 +26,7 @@ import ( "sync" "github.com/purpleidea/mgmt/engine" + "github.com/purpleidea/mgmt/engine/local" "github.com/purpleidea/mgmt/lang/ast" _ "github.com/purpleidea/mgmt/lang/funcs/core" // import so the funcs register "github.com/purpleidea/mgmt/lang/funcs/dage" @@ -57,6 +58,7 @@ type Lang struct { Input string Hostname string + Local *local.API World engine.World Prefix string Debug bool @@ -139,6 +141,7 @@ func (obj *Lang) Init() error { LexParser: parser.LexParse, Downloader: nil, // XXX: is this used here? StrInterpolater: interpolate.StrInterpolate, + //Local: obj.Local, // TODO: do we need this? //World: obj.World, // TODO: do we need this? Prefix: obj.Prefix, @@ -237,6 +240,7 @@ func (obj *Lang) Init() error { obj.funcs = &dage.Engine{ Name: "lang", // TODO: arbitrary name for now Hostname: obj.Hostname, + Local: obj.Local, World: obj.World, //Prefix: fmt.Sprintf("%s/", path.Join(obj.Prefix, "funcs")), Debug: obj.Debug, diff --git a/lib/main.go b/lib/main.go index 6bb39f44..0e2ed521 100644 --- a/lib/main.go +++ b/lib/main.go @@ -36,6 +36,7 @@ import ( "github.com/purpleidea/mgmt/engine" "github.com/purpleidea/mgmt/engine/graph" "github.com/purpleidea/mgmt/engine/graph/autogroup" + "github.com/purpleidea/mgmt/engine/local" _ "github.com/purpleidea/mgmt/engine/resources" // let register's run "github.com/purpleidea/mgmt/etcd" "github.com/purpleidea/mgmt/etcd/chooser" @@ -475,6 +476,15 @@ func (obj *Main) Run() error { } }() + // implementation of the Local API (we only expect just this single one) + localAPI := &local.API{ + Prefix: fmt.Sprintf("%s/", path.Join(prefix, "local")), + Debug: obj.Flags.Debug, + Logf: func(format string, v ...interface{}) { + log.Printf("local: api: "+format, v...) + }, + } + // implementation of the World API (alternatives can be substituted in) world := &etcd.World{ Hostname: hostname, @@ -493,6 +503,7 @@ func (obj *Main) Run() error { Version: obj.Version, Hostname: hostname, Converger: converger, + Local: localAPI, World: world, Prefix: fmt.Sprintf("%s/", path.Join(prefix, "engine")), //Prometheus: prom, // TODO: implement this via a general Status API @@ -582,6 +593,7 @@ func (obj *Main) Run() error { Program: obj.Program, Version: obj.Version, Hostname: hostname, + Local: localAPI, World: world, Noop: mainDeploy.Noop, // FIXME: should the below flags come from the deploy struct?