feat: Execute commands and attach to STDOUT/STDERR

This commit is contained in:
christiangoeschel 2025-03-30 23:15:33 -04:00
parent c1ef52f713
commit 4e2ddf629d
Signed by: christiangoeschel
GPG Key ID: 9C5DF8B5AF67BFB2
5 changed files with 91 additions and 21 deletions

View File

@ -19,16 +19,53 @@ package api
import (
"fmt"
"github.com/gin-gonic/gin"
"io"
"net/http"
"os/exec"
)
func runG10K(c *gin.Context) {
fmt.Printf("Authorized call :)")
cmd := exec.Command("/usr/bin/g10k", "-config", "/etc/puppetlabs/g10k/g10k.yaml")
cmd.Run()
cmd := exec.Command("/usr/local/bin/g10k -config /etc/puppetlabs/g10k/g10k.yaml'")
stdout, err := cmd.StdoutPipe()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": "Failed",
"error": err})
return
}
stderr, err := cmd.StderrPipe()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": "Failed",
"error": err})
return
}
if err = cmd.Start(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": "Failed",
"error": err})
return
}
fmt.Printf("Started g10k ...\n")
stdOut, _ := io.ReadAll(stdout)
stdErr, _ := io.ReadAll(stderr)
if err = cmd.Wait(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"status": "Failed",
"error": err})
return
}
c.JSON(http.StatusOK, gin.H{
"success": "Authorized :D",
"command": "g10k -config /etc/puppetlabs/g10k/g10k.yaml"})
"status": "Success",
"stdout": string(stdOut),
"stderr": string(stdErr),
"error": "",
})
return
}

View File

@ -21,34 +21,52 @@ import (
"fmt"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
"net/http"
)
func SetupRouter(config settings.Config) error {
func Start(config settings.Config) error {
router := gin.Default()
netAddr := config.Network.ListenAddress + ":" + strconv.Itoa(config.Network.ListenPort)
router.POST("/g10k", authMiddleware(config.API.AuthMode, config.API.Token))
s := &http.Server{
Addr: netAddr,
Handler: router,
ReadTimeout: 2 * time.Second, // this works because constants have an adaptive type
WriteTimeout: time.Duration(config.Network.ResponseTimeout) * time.Second, // here we have to do a type conversion
MaxHeaderBytes: 1 << 20,
}
if err := startRouter(router, netAddr); err != nil {
router.POST("/g10k", authMiddleware(runG10K, config.API.AuthMode, config.API.Token))
if err := serve(s, config); err != nil {
return fmt.Errorf("Could not start router %s", err)
}
return nil
}
func startRouter(r *gin.Engine, a string) error {
return r.Run(a)
func serve(s *http.Server, c settings.Config) error {
e := c.Network.EnableTLS
crt := c.Network.TLSCertFile
k := c.Network.TLSKeyFile
if !e {
fmt.Printf("Serving the API via HTTP on %s\n", s.Addr)
return s.ListenAndServe()
} else {
fmt.Printf("Serveing the API via HTTPS on %s\n", s.Addr)
return s.ListenAndServeTLS(crt, k)
}
}
func authMiddleware(m bool, t string) gin.HandlerFunc {
func authMiddleware(fn gin.HandlerFunc, m bool, t string) gin.HandlerFunc {
return func(c *gin.Context) {
// authMode == false bypasses token validation check
if m == false {
runG10K(c)
return
fn(c)
}
authHeader := c.GetHeader("Authorization")
@ -74,6 +92,6 @@ func authMiddleware(m bool, t string) gin.HandlerFunc {
return
}
runG10K(c)
fn(c)
}
}

View File

@ -32,7 +32,10 @@ var (
configFile string
netAddr string
port int
responseTimeout int
enableTLS bool
tlsKeyFile string
tlsCertFile string
authMode bool
token string
tokenHashAlgo string
@ -69,7 +72,10 @@ func init() {
serverCmd.PersistentFlags().StringVarP(&configFile, "config", "c", "", "configuration file to use")
serverCmd.PersistentFlags().StringVarP(&netAddr, "address", "a", settings.SetDefault().Network.ListenAddress, "network address (e.g. 0.0.0.0)")
serverCmd.PersistentFlags().IntVarP(&port, "port", "p", settings.SetDefault().Network.ListenPort, "port to listen on")
serverCmd.PersistentFlags().IntVar(&responseTimeout, "timeout", settings.SetDefault().Network.ResponseTimeout, "API request response timeout (sec)")
serverCmd.PersistentFlags().BoolVarP(&enableTLS, "tls", "s", settings.SetDefault().Network.EnableTLS, "enable TLS")
serverCmd.PersistentFlags().StringVar(&tlsCertFile, "cert", settings.SetDefault().Network.TLSCertFile, "TLS cert file path")
serverCmd.PersistentFlags().StringVar(&tlsKeyFile, "key", settings.SetDefault().Network.TLSKeyFile, "TLS key file path")
serverCmd.PersistentFlags().BoolVar(&authMode, "auth", settings.SetDefault().API.AuthMode, "enable verbose output for debugging")
serverCmd.PersistentFlags().StringVarP(&token, "token", "t", settings.SetDefault().API.Token, "API token (implies --auth)")
tokenCmd.PersistentFlags().StringVarP(&tokenHashAlgo, "algorithm", "a", settings.SetDefault().API.TokenHashAlgorithm, "specify the token hashing algorithm")
@ -77,7 +83,10 @@ func init() {
viper.BindPFlag("logging.debug", rootCmd.PersistentFlags().Lookup("debug"))
viper.BindPFlag("network.address", serverCmd.PersistentFlags().Lookup("address"))
viper.BindPFlag("network.port", serverCmd.PersistentFlags().Lookup("port"))
viper.BindPFlag("network.responseTimeout", serverCmd.PersistentFlags().Lookup("timeout"))
viper.BindPFlag("network.enableTLS", serverCmd.PersistentFlags().Lookup("tls"))
viper.BindPFlag("network.tlsCert", serverCmd.PersistentFlags().Lookup("key"))
viper.BindPFlag("network.tlsKey", serverCmd.PersistentFlags().Lookup("cert"))
viper.BindPFlag("api.authMode", serverCmd.PersistentFlags().Lookup("auth"))
viper.BindPFlag("api.token", serverCmd.PersistentFlags().Lookup("token"))
viper.BindPFlag("api.tokenHashAlgorithm", tokenCmd.PersistentFlags().Lookup("algorithm"))

View File

@ -38,7 +38,7 @@ var serverCmd = &cobra.Command{
}
func serverStart() error {
if err := api.SetupRouter(config); err != nil {
if err := api.Start(config); err != nil {
return err
}

View File

@ -17,9 +17,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package settings
type Network struct {
ListenAddress string `mapstructure:"address"`
ListenPort int `mapstructure:"port"`
EnableTLS bool `mapstructure:"enableTLS"`
ListenAddress string `mapstructure:"address"`
ListenPort int `mapstructure:"port"`
ResponseTimeout int `mapstructure:"responseTimeout"`
EnableTLS bool `mapstructure:"enableTLS"`
TLSCertFile string `mapstructure:"tlsCert"`
TLSKeyFile string `mapstructure:"tlsKey"`
}
type API struct {
@ -41,9 +44,12 @@ type Config struct {
func SetDefault() Config {
return Config{
Network: Network{
ListenAddress: "127.0.0.1",
ListenPort: 8080,
EnableTLS: false,
ListenAddress: "127.0.0.1",
ListenPort: 8080,
ResponseTimeout: 60,
EnableTLS: false,
TLSCertFile: "",
TLSKeyFile: "",
},
API: API{
AuthMode: false,