File: builtin.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 (144 lines) | stat: -rw-r--r-- 3,313 bytes parent folder | download | duplicates (3)
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
package plugin

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/hashicorp/go-hclog"
	"github.com/hashicorp/go-plugin"
	"github.com/oklog/run"

	sdk "github.com/hashicorp/vagrant-plugin-sdk"
	"github.com/hashicorp/vagrant-plugin-sdk/component"
	"github.com/hashicorp/vagrant-plugin-sdk/internal-shared/pluginclient"
)

type Builtin struct {
	group   run.Group
	servers map[string]*plugin.ReattachConfig
	log     hclog.Logger
	cancel  context.CancelFunc
	ctx     context.Context
	mu      sync.Mutex
}

func NewBuiltins(ctx context.Context, log hclog.Logger) *Builtin {
	ctx, cancel := context.WithCancel(ctx)
	return &Builtin{
		log:     log.Named("builtins"),
		cancel:  cancel,
		ctx:     ctx,
		servers: map[string]*plugin.ReattachConfig{},
	}
}

func (b *Builtin) ConnectInfo(name string) (*plugin.ReattachConfig, error) {
	// TODO(spox): need to update with this channel and cancelable context
	for i := 0; i < 5; i++ {
		b.mu.Lock()
		r, ok := b.servers[name]
		b.mu.Unlock()
		if ok {
			return r, nil
		}
		time.Sleep(1 * time.Second)
	}

	b.log.Error("failed to locate plugin", "name", name, "servers", b.servers)
	return nil, fmt.Errorf("unknown builtin plugin %s", name)
}

func (b *Builtin) Add(name string, opts ...sdk.Option) (f PluginRegistration, err error) {
	clCh := make(chan struct{})
	reCh := make(chan *plugin.ReattachConfig)
	cfg := &plugin.ServeTestConfig{
		Context:          b.ctx,
		ReattachConfigCh: reCh,
		CloseCh:          clCh,
	}

	// Add our options to keep it running in process
	opts = append(opts, sdk.InProcess(cfg), sdk.WithLogger(b.log))

	// Spin off a new go routine to get the reattach config
	go func() {
		rc := <-reCh
		b.mu.Lock()
		defer b.mu.Unlock()
		b.servers[name] = rc
	}()

	// Add the plugin server to our group
	b.group.Add(func() error {
		sdk.Main(opts...)
		return nil
	}, func(err error) {
		b.log.Warn("builtin group has exited", "error", err)
		b.cancel()
	})

	return b.Factory(name), nil
}

func (b *Builtin) Start() {
	go b.group.Run()
}

func (b *Builtin) Close() {
	b.cancel()
}

func (b *Builtin) Factory(name string) PluginRegistration {
	return func(log hclog.Logger) (p *Plugin, err error) {
		r, err := b.ConnectInfo(name)
		if err != nil {
			return nil, err
		}
		config := pluginclient.ClientConfig(b.log.Named(name))
		config.Logger = b.log.Named(name)
		config.Reattach = r
		config.Plugins = config.VersionedPlugins[1]
		client := plugin.NewClient(config)
		rpcClient, err := client.Client()
		if err != nil {
			b.log.Error("failed to create rpc client for builtin",
				"name", name,
				"error", err)

			rpcClient.Close()
			return nil, err
		}

		raw, err := rpcClient.Dispense("plugininfo")
		if err != nil {
			log.Error("error requesting builtin plugin information interface",
				"plugin", name,
				"error", err)

			return
		}
		info, ok := raw.(component.PluginInfo)
		if !ok {
			return nil, fmt.Errorf("failed to load builgin plugin information interface \nplugin %v", name)
		}

		p = &Plugin{
			Builtin:  false,
			Client:   rpcClient,
			Location: fmt.Sprintf("builtin::%s", name),
			Name:     info.Name(),
			Types:    info.ComponentTypes(),
			logger:   log,
			src:      client,
		}

		// Close the rpcClient when plugin is closed
		p.Closer(func() error {
			return rpcClient.Close()
		})

		return
	}
}