File: dynamic.go

package info (click to toggle)
vagrant 2.3.7%2Bgit20230731.5fc64cde%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 17,616 kB
  • sloc: ruby: 111,820; sh: 462; makefile: 123; ansic: 34; lisp: 1
file content (146 lines) | stat: -rw-r--r-- 4,009 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package cli

import (
	"context"
	"fmt"

	"google.golang.org/genproto/googleapis/rpc/errdetails"
	"google.golang.org/grpc/status"

	"github.com/hashicorp/vagrant-plugin-sdk/component"
	"github.com/hashicorp/vagrant-plugin-sdk/proto/vagrant_plugin_sdk"
	"github.com/hashicorp/vagrant-plugin-sdk/terminal"
	"github.com/hashicorp/vagrant/internal/client"
	"github.com/hashicorp/vagrant/internal/server/proto/vagrant_server"
)

type DynamicCommand struct {
	*baseCommand

	name     string
	synopsis string
	help     string
	parent   *DynamicCommand
	flags    []*component.CommandFlag
	primary  bool
}

func (c *DynamicCommand) Run(args []string) int {
	if err := c.Init(
		WithArgs(args),
		WithFlags(c.Flags()),
	); err != nil {
		return 1
	}

	var r *vagrant_server.Job_CommandResult
	err := c.Do(c.Ctx, func(ctx context.Context, cl *client.Client, modifier client.JobModifier) (err error) {
		cmdArgs := &vagrant_plugin_sdk.Command_Arguments{
			Args:  c.args,
			Flags: []*vagrant_plugin_sdk.Command_Arguments_Flag{},
		}
		for f, v := range c.flagData {
			cmdFlag := &vagrant_plugin_sdk.Command_Arguments_Flag{Name: f.LongName}
			switch f.Type {
			case component.FlagBool:
				cmdFlag.Type = vagrant_plugin_sdk.Command_Arguments_Flag_BOOL
				cmdFlag.Value = &vagrant_plugin_sdk.Command_Arguments_Flag_Bool{
					Bool: v.(bool),
				}
			case component.FlagString:
				cmdFlag.Type = vagrant_plugin_sdk.Command_Arguments_Flag_STRING
				cmdFlag.Value = &vagrant_plugin_sdk.Command_Arguments_Flag_String_{
					String_: v.(string),
				}
			}
			cmdArgs.Flags = append(cmdArgs.Flags, cmdFlag)
		}

		c.Log.Debug("collected argument flags",
			"flags", cmdArgs.Flags,
			"args", args,
			"remaining", c.args,
		)

		cOp := &vagrant_server.Job_CommandOp{
			Command: c.name,
			Component: &vagrant_server.Component{
				Type: vagrant_server.Component_COMMAND,
				Name: c.name,
			},
			CliArgs: cmdArgs,
		}

		r, err = cl.Command(ctx, cOp, modifier)

		// If nothing failed but we didn't get a Result back, something may
		// have gone wrong on the far side so we need to interpret the error.
		if err == nil && !r.RunResult {
			runErrorStatus := status.FromProto(r.RunError)
			details := runErrorStatus.Details()
			userError := false
			for _, msg := range details {
				switch m := msg.(type) {
				case *errdetails.LocalizedMessage:
					// Errors from Ruby with LocalizedMessages are user-facing,
					// so can be output directly.
					userError = true
					cl.UI().Output(m.Message, terminal.WithErrorStyle())
					// All user-facing errors from Ruby use a 1 exit code. See
					// Vagrant::Errors::VagrantError.
					r.ExitCode = 1
				}
			}
			// If there wasn't a user-facing error, just assign the returned
			// error (if any) from the response and assign that back out so it
			// can be displayed as an unexpected error.
			if !userError {
				err = runErrorStatus.Err()
			}
		}

		if err != nil {
			cl.UI().Output("Running of task "+c.name+" failed unexpectedly\n", terminal.WithErrorStyle())
			cl.UI().Output("Error: "+err.Error(), terminal.WithErrorStyle())
		}

		c.Log.Debug("result from operation", "task", c.name, "result", r)

		return err
	})

	if err != nil {
		c.Log.Error("Got error from task, so exiting 255", "error", err)
		return int(-1)
	}

	c.Log.Info("Task did not error, so exiting with provided code", "code", r.ExitCode)
	return int(r.ExitCode)
}

func (c *DynamicCommand) Synopsis() string {
	return c.synopsis
}

func (c *DynamicCommand) Help() string {
	fset := c.generateCliFlags(c.Flags())
	return formatHelp(fmt.Sprintf("%s\n%s\n", c.help, fset.Display()))
}

func (c *DynamicCommand) Flags() component.CommandFlags {
	return c.flagSet(flagSetOperation, func(opts []*component.CommandFlag) []*component.CommandFlag {
		return append(c.flags, opts...)
	})
}

func (c *DynamicCommand) Primary() bool {
	return c.primary
}

func (c *DynamicCommand) fullName() string {
	var v string
	if c.parent != nil {
		v = c.parent.fullName() + " "
	}
	return v + c.name
}