Adds cloud subcommand with some toy options

This commit is contained in:
Brandon Croft 2023-06-28 18:23:38 -06:00
parent 7111fd1170
commit 2c3d134705
No known key found for this signature in database
GPG Key ID: B01E32423322EB9D
3 changed files with 134 additions and 0 deletions

View File

@ -414,6 +414,14 @@ func initCommands(
},
}
if meta.AllowExperimentalFeatures {
Commands["cloud"] = func() (cli.Command, error) {
return &command.CloudCommand{
Meta: meta,
}, nil
}
}
PrimaryCommands = []string{
"init",
"validate",

102
internal/command/cloud.go Normal file
View File

@ -0,0 +1,102 @@
package command
import (
"bytes"
"fmt"
"io"
"os"
"os/exec"
"github.com/hashicorp/go-plugin"
"github.com/hashicorp/terraform/internal/cloudplugin"
"github.com/hashicorp/terraform/internal/cloudplugin/cloudplugin1"
"github.com/hashicorp/terraform/internal/logging"
)
// CloudCommand is a Command implementation that interacts with Terraform
// Cloud for operations that are inherently planless. It delegates
// all execution to an internal plugin.
type CloudCommand struct {
Meta
}
const (
// DefaultCloudPluginVersion is the implied protocol version, though all
// historical versions are defined explicitly.
DefaultCloudPluginVersion = 1
// ExitRPCError is the exit code that is returned if an plugin
// communication error occurred.
ExitRPCError = 99
)
var (
// Handshake is used to verify that the plugin is the appropriate plugin for
// the client. This is not a security verification.
Handshake = plugin.HandshakeConfig{
MagicCookieKey: "TF_CLOUDPLUGIN_MAGIC_COOKIE",
MagicCookieValue: "721fca41431b780ff3ad2623838faaa178d74c65e1cfdfe19537c31656496bf9f82d6c6707f71d81c8eed0db9043f79e56ab4582d013bc08ead14f57961461dc",
ProtocolVersion: DefaultCloudPluginVersion,
}
)
func (c *CloudCommand) proxy(args []string, stdout, stderr io.Writer) int {
client := plugin.NewClient(&plugin.ClientConfig{
HandshakeConfig: Handshake,
AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
Cmd: exec.Command("./terraform-cloudplugin"),
Logger: logging.NewCloudLogger(),
VersionedPlugins: map[int]plugin.PluginSet{
1: {
"cloud": &cloudplugin1.GRPCCloudPlugin{},
},
},
})
defer client.Kill()
// Connect via RPC
rpcClient, err := client.Client()
if err != nil {
fmt.Fprintf(stderr, "Failed to create cloud plugin client: %s", err)
return ExitRPCError
}
// Request the plugin
raw, err := rpcClient.Dispense("cloud")
if err != nil {
fmt.Fprintf(stderr, "Failed to request cloud plugin interface: %s", err)
return ExitRPCError
}
// Proxy the request
cloud1 := raw.(cloudplugin.Cloud1)
return cloud1.Execute(args, stdout, stderr)
}
// Run runs the cloud command with the given arguments.
func (c *CloudCommand) Run(args []string) int {
args = c.Meta.process(args)
// TODO: Download and verify the signing of the terraform-cloudplugin
// release that is appropriate for this OS/Arch
if _, err := os.Stat("./terraform-cloudplugin"); err != nil {
c.Ui.Warn("terraform-cloudplugin not found. This plugin does not have an official release, yet.")
return 1
}
// TODO: Need to use some type of c.Meta handle here
return c.proxy(args, os.Stdout, os.Stderr)
}
// Help returns help text for the cloud command.
func (c *CloudCommand) Help() string {
helpText := new(bytes.Buffer)
c.proxy([]string{}, helpText, io.Discard)
return helpText.String()
}
// Synopsis returns a short summary of the cloud command.
func (c *CloudCommand) Synopsis() string {
return "Manage Terraform Cloud settings and metadata"
}

View File

@ -30,6 +30,7 @@ const (
// to other loggers, like provisioners and remote-state backends.
envLogCore = "TF_LOG_CORE"
envLogProvider = "TF_LOG_PROVIDER"
envLogCloud = "TF_LOG_CLOUD"
)
var (
@ -134,6 +135,20 @@ func NewProviderLogger(prefix string) hclog.Logger {
return l
}
// NewCloudLogger returns a logger for the cloud plugin, possibly with a
// different log level from the global logger.
func NewCloudLogger() hclog.Logger {
l := &logPanicWrapper{
Logger: logger.Named("cloud"),
}
level := cloudLogLevel()
logger.Debug("created cloud logger", "level", level)
l.SetLevel(level)
return l
}
// CurrentLogLevel returns the current log level string based the environment vars
func CurrentLogLevel() string {
ll, _ := globalLogLevel()
@ -149,6 +164,15 @@ func providerLogLevel() hclog.Level {
return parseLogLevel(providerEnvLevel)
}
func cloudLogLevel() hclog.Level {
providerEnvLevel := strings.ToUpper(os.Getenv(envLogCloud))
if providerEnvLevel == "" {
providerEnvLevel = strings.ToUpper(os.Getenv(envLog))
}
return parseLogLevel(providerEnvLevel)
}
func globalLogLevel() (hclog.Level, bool) {
var json bool
envLevel := strings.ToUpper(os.Getenv(envLog))