Example #1
0
func TestEmptyPlugin(t *testing.T) {
	transport, done := fakeEnvelopeClient()
	defer done()

	go Main(&Plugin{Name: "hello"})

	client := api.NewPluginClient(multiplex.NewClient("Plugin", transport))

	response, err := client.Handshake(&api.HandshakeRequest{})
	require.NoError(t, err)
	assert.Equal(t, api.APIVersion, response.APIVersion)
	assert.Equal(t, "hello", response.Name)
	assert.Equal(t, version.Version, *response.LibraryVersion)
	assert.Empty(t, response.Features)

	assert.NoError(t, client.Goodbye())
}
Example #2
0
func (h *transportHandle) ServiceGenerator() ServiceGenerator {
	if !h.Running.Load() {
		panic(fmt.Sprintf("handle for plugin %q has already been closed", h.name))
	}

	if _, hasFeature := h.Features[api.FeatureServiceGenerator]; !hasFeature {
		return nil
	}

	return &serviceGenerator{
		handle:  h,
		Running: h.Running,
		ServiceGenerator: api.NewServiceGeneratorClient(multiplex.NewClient(
			"ServiceGenerator",
			envelope.NewClient(_proto, h.Transport),
		)),
	}
}
Example #3
0
func TestServiceGenerator(t *testing.T) {
	transport, done := fakeEnvelopeClient()
	defer done()

	mockCtrl := gomock.NewController(t)
	defer mockCtrl.Finish()

	serviceGenerator := plugintest.NewMockServiceGenerator(mockCtrl)

	go Main(&Plugin{
		Name:             "hello",
		ServiceGenerator: serviceGenerator,
	})

	pluginClient := api.NewPluginClient(multiplex.NewClient("Plugin", transport))
	defer pluginClient.Goodbye()

	handshake, err := pluginClient.Handshake(&api.HandshakeRequest{})
	require.NoError(t, err)
	assert.Equal(t, api.APIVersion, handshake.APIVersion)
	assert.Equal(t, version.Version, *handshake.LibraryVersion)
	assert.Equal(t, "hello", handshake.Name)
	assert.Contains(t, handshake.Features, api.FeatureServiceGenerator)

	sgClient := api.NewServiceGeneratorClient(multiplex.NewClient("ServiceGenerator", transport))
	req := &api.GenerateServiceRequest{
		RootServices: []api.ServiceID{1},
		Services: map[api.ServiceID]*api.Service{
			1: {
				Name:       "MyService",
				ThriftName: "MyService",
				Functions:  []*api.Function{},
				ParentID:   (*api.ServiceID)(ptr.Int32(2)),
				ModuleID:   1,
			},
			2: {
				Name:       "BaseService",
				ThriftName: "BaseService",
				Functions: []*api.Function{
					{
						Name:       "Healthy",
						ThriftName: "healthy",
						Arguments:  []*api.Argument{},
					},
				},
				ModuleID: 1,
			},
		},
		Modules: map[api.ModuleID]*api.Module{
			1: {
				ImportPath: "go.uber.org/thriftrw/plugin/fake",
				Directory:  "fake",
			},
		},
	}

	res := &api.GenerateServiceResponse{
		Files: map[string][]byte{
			"fake/myservice/foo.go":   {1, 2, 3},
			"fake/baseservice/bar.go": {4, 5, 6},
			"fake/baz.go":             {7, 8, 9},
		},
	}

	serviceGenerator.EXPECT().Generate(req).Return(res, nil)
	gotRes, err := sgClient.Generate(req)
	if assert.NoError(t, err) {
		assert.Equal(t, res, gotRes)
	}
}
Example #4
0
// NewTransportHandle builds a new Handle which speaks to the given transport.
//
// If the transport is an io.Closer, it will be closed when the handle is closed.
func NewTransportHandle(name string, t envelope.Transport) (Handle, error) {
	client := api.NewPluginClient(multiplex.NewClient(
		"Plugin",
		envelope.NewClient(_proto, t),
	))

	handshake, err := client.Handshake(&api.HandshakeRequest{})
	if err != nil {
		return nil, errHandshakeFailed{Name: name, Reason: err}
	}

	if handshake.Name != name {
		return nil, errHandshakeFailed{
			Name:   name,
			Reason: errNameMismatch{Want: name, Got: handshake.Name},
		}
	}

	if handshake.APIVersion != api.APIVersion {
		return nil, errHandshakeFailed{
			Name:   name,
			Reason: errAPIVersionMismatch{Want: api.APIVersion, Got: handshake.APIVersion},
		}
	}

	// If we got here, the API version matches so the plugin must have
	// provided the Version
	if handshake.LibraryVersion == nil {
		return nil, errHandshakeFailed{
			Name:   name,
			Reason: errVersionIsRequired,
		}
	}

	version, err := semver.Parse(*handshake.LibraryVersion)
	if err != nil {
		return nil, errHandshakeFailed{Name: name, Reason: err}
	}

	if !compatRange.Contains(version) {
		return nil, errHandshakeFailed{
			Name: name,
			Reason: errVersionMismatch{
				Want: compatRange,
				Got:  *handshake.LibraryVersion,
			},
		}
	}

	features := make(map[api.Feature]struct{}, len(handshake.Features))
	for _, feature := range handshake.Features {
		features[feature] = struct{}{}
	}

	return &transportHandle{
		name:      name,
		Transport: t,
		Client:    client,
		Running:   atomic.NewBool(true),
		Features:  features,
	}, nil
}