Skip to content

Commit

Permalink
feat: add ability to check version in test, and upgrade if necessary
Browse files Browse the repository at this point in the history
  • Loading branch information
mefellows committed Mar 14, 2021
1 parent fde75d0 commit bb17299
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 82 deletions.
2 changes: 1 addition & 1 deletion command/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ var installCmd = &cobra.Command{

func init() {
installCmd.Flags().BoolVarP(&force, "force", "f", false, "Force a new installation")
installCmd.Flags().StringVarP(&libDir, "libDir", "d", "/usr/local/lib", "Target directory to install the library")
installCmd.Flags().StringVarP(&libDir, "libDir", "d", "", "Target directory to install the library")
RootCmd.AddCommand(installCmd)
}
1 change: 1 addition & 0 deletions examples/v3/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var pactDir = fmt.Sprintf("%s/pacts", dir)
// 2. go test -v -run TestProvider
func TestV3HTTPProvider(t *testing.T) {
v3.SetLogLevel("DEBUG")
v3.CheckVersion()

// Start provider API in the background
go startServer()
Expand Down
18 changes: 11 additions & 7 deletions v3/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ import (
"time"

"github.com/pact-foundation/pact-go/utils"
"github.com/pact-foundation/pact-go/v3/internal/native/mockserver"
native "github.com/pact-foundation/pact-go/v3/internal/native/mockserver"
)

func init() {
initLogging()
native.Init()
}

// QueryStringStyle allows a user to specific the v2 query string serialisation format
Expand Down Expand Up @@ -121,7 +121,8 @@ type httpMockProvider struct {
v3Interactions []*InteractionV3

// fsm state of the interaction
state string
state string
mockserver *mockserver.MockServer
}

// MockServerConfig stores the address configuration details of the server for the current executing test
Expand Down Expand Up @@ -168,6 +169,9 @@ func (p *httpMockProvider) validateConfig() error {
return fmt.Errorf("error: unable to find free port, mock server will fail to start")
}

p.mockserver = &mockserver.MockServer{}
p.mockserver.Init()

return nil
}

Expand Down Expand Up @@ -195,8 +199,8 @@ func (p *httpMockProvider) ExecuteTest(integrationTest func(MockServerConfig) er
// Clean interactions
p.cleanInteractions()

port, err := native.CreateMockServer(formatJSONObject(serialisedPact), fmt.Sprintf("%s:%d", p.config.Host, p.config.Port), p.config.TLS)
defer native.CleanupMockServer(p.config.Port)
port, err := p.mockserver.CreateMockServer(formatJSONObject(serialisedPact), fmt.Sprintf("%s:%d", p.config.Host, p.config.Port), p.config.TLS)
defer p.mockserver.CleanupMockServer(p.config.Port)
if err != nil {
return err
}
Expand All @@ -213,7 +217,7 @@ func (p *httpMockProvider) ExecuteTest(integrationTest func(MockServerConfig) er
}

// Run Verification Process
res, mismatches := native.Verify(p.config.Port, p.config.PactDir)
res, mismatches := p.mockserver.Verify(p.config.Port, p.config.PactDir)
p.displayMismatches(mismatches)

if !res {
Expand All @@ -225,7 +229,7 @@ func (p *httpMockProvider) ExecuteTest(integrationTest func(MockServerConfig) er

// TODO: pretty print this to make it really easy to understand the problems
// See existing Pact/Ruby code examples
func (p *httpMockProvider) displayMismatches(mismatches []native.MismatchedRequest) {
func (p *httpMockProvider) displayMismatches(mismatches []mockserver.MismatchedRequest) {
if len(mismatches) > 0 {
log.Println("[INFO] pact validation failed, errors: ")
for _, m := range mismatches {
Expand Down Expand Up @@ -259,7 +263,7 @@ func (p *httpMockProvider) displayMismatches(mismatches []native.MismatchedReque
func (p *httpMockProvider) WritePact() error {
log.Println("[DEBUG] write pact file")
if p.config.Port != 0 {
return native.WritePactFile(p.config.Port, p.config.PactDir)
return p.mockserver.WritePactFile(p.config.Port, p.config.PactDir)
}
return errors.New("pact server not yet started")
}
Expand Down
109 changes: 52 additions & 57 deletions v3/installer/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package installer
import (
"fmt"
"log"
"os"
"os/exec"
"path"
"runtime"
Expand All @@ -13,20 +14,9 @@ import (
getter "github.com/hashicorp/go-getter"
goversion "github.com/hashicorp/go-version"

// can't use these packages, because then the CLI installer wouldn't work - go won't run without it!
// "github.com/pact-foundation/pact-go/v3/internal/native/verifier"
// mockserver "github.com/pact-foundation/pact-go/v3/internal/native/mockserver"

"github.com/spf13/afero"
)

// Installer manages the underlying Ruby installation
// (eventual) implementation requirements
// 1. Download OS specific artifacts if not pre-installed - DONE
// 1. Check the semver range of pre-installed artifacts - DONE
// 1. Enable global configuration (environment vars, config files, code options e.g. (`PACT_GO_SHARED_LIBRARY_PATH`))
// 1. Allow users to specify where they pre-install their artifacts (e.g. /usr/local/lib)

// Installer is used to check the Pact Go installation is setup correctly, and can automatically install
// packages if required
type Installer struct {
Expand Down Expand Up @@ -80,18 +70,13 @@ func (i *Installer) Force(force bool) {
func (i *Installer) CheckInstallation() error {

// Check if files exist
// --> Check versions of existing installed files
// --> Check if existing installed files
if !i.force {
if err := i.checkPackageInstall(); err == nil {
return nil
}
}

// Check if override package path exists
// -> if it does, copy files from existing location
// --> Check versions of these files
// --> copy files to lib dir

// Download dependencies
if err := i.downloadDependencies(); err != nil {
return err
Expand All @@ -102,39 +87,49 @@ func (i *Installer) CheckInstallation() error {
return err
}

// Double check files landed correctly (can't execute 'version' call here,
// because of dependency on the native libs we're trying to download!)
if err := i.checkPackageInstall(); err != nil {
return fmt.Errorf("unable to verify downloaded/installed dependencies: %s", err)
}

// --> Check if download is disabled (return error if downloads are disabled)
// --> download files to lib dir

return nil
}

func (i *Installer) getLibDir() string {
if i.libDir == "" {
return "/usr/local/lib"
if i.libDir != "" {
return i.libDir
}

return i.libDir
env := os.Getenv(downloadEnvVar)
if env != "" {
return env
}

return "/usr/local/lib"
}

// checkPackageInstall discovers any existing packages, and checks installation of a given binary using semver-compatible checks
func (i *Installer) checkPackageInstall() error {
for pkg, info := range packages {

log.Println("[DEBUG] checking version for lib", info.libName, "semver range", info.semverRange)
dst, _ := i.getLibDstForPackage(pkg)

if _, err := i.fs.Stat(dst); err != nil {
log.Println("[DEBUG] package", info.libName, "not found")
return err
}

// if err := checkVersion(info.libName, info.testCommand(), info.semverRange); err != nil {
// return err
// }
lib, ok := LibRegistry[pkg]

if ok {
log.Println("[INFO] checking version", lib.Version(), "for lib", info.libName, "within semver range", info.semverRange)
if err := checkVersion(info.libName, lib.Version(), info.semverRange); err != nil {
return err
}
} else {
log.Println("[DEBUG] lib registry is currently not populated for package", pkg, "this is probably because the package is currently being installed")
}
}

return nil
Expand Down Expand Up @@ -226,6 +221,27 @@ var setOSXInstallName = func(file string, lib string) error {
return err
}

func checkVersion(lib, version, versionRange string) error {
log.Println("[DEBUG] checking version", version, "of", lib, "against semver constraint", versionRange)

v, err := goversion.NewVersion(version)
if err != nil {
return err
}

constraints, err := goversion.NewConstraint(versionRange)
if err != nil {
return err
}

if constraints.Check(v) {
log.Println("[DEBUG]", v, "satisfies constraints", v, constraints)
return nil
}

return fmt.Errorf("version %s of %s does not match constraint %s", version, lib, versionRange)
}

// download template structure: "https://github.com/pact-foundation/pact-reference/releases/download/PACKAGE-vVERSION/LIBNAME-OS-ARCH.EXTENSION.gz"
var downloadTemplate = "https://github.com/pact-foundation/pact-reference/releases/download/%s-v%s/%s-%s-%s.%s.gz"

Expand All @@ -245,58 +261,37 @@ type packageInfo struct {
libName string
version string
semverRange string
testCommand func() string
}

const (
verifierPackage = "pact_verifier_ffi"
mockServerPackage = "libpact_mock_server_ffi"
VerifierPackage = "pact_verifier_ffi"
MockServerPackage = "libpact_mock_server_ffi"
downloadEnvVar = "PACT_GO_LIB_DOWNLOAD_PATH"
linux = "linux"
windows = "windows"
osx = "osx"
x86_64 = "x86_64"
)

var packages = map[string]packageInfo{
verifierPackage: {
VerifierPackage: {
libName: "libpact_verifier_ffi",
version: "0.0.2",
semverRange: ">= 0.0.2, < 1.0.0",
// testCommand: func() string {
// return (&verifier.Verifier{}).Version()
// },
},
mockServerPackage: {
MockServerPackage: {
libName: "libpact_mock_server_ffi",
version: "0.0.15",
semverRange: ">= 0.0.15, < 1.0.0",
// testCommand: func() string {
// return mockserver.Version()
// },
},
}

func checkVersion(lib, version, versionRange string) error {
log.Println("[DEBUG] checking version", version, "of", lib, "against semver constraint", versionRange)

v, err := goversion.NewVersion(version)
if err != nil {
return err
}

constraints, err := goversion.NewConstraint(versionRange)
if err != nil {
return err
}

if constraints.Check(v) {
log.Println("[DEBUG]", v, "satisfies constraints", v, constraints)
return nil
}

return fmt.Errorf("version %s of %s does not match constraint %s", version, lib, versionRange)
type Versioner interface {
Version() string
}

var LibRegistry = map[string]Versioner{}

type downloader interface {
download(src string, dst string) error
}
Expand Down
10 changes: 5 additions & 5 deletions v3/installer/installer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestInstallerDownloader(t *testing.T) {
}{
{
name: "mock server - linux x86",
pkg: mockServerPackage,
pkg: MockServerPackage,
want: "https://github.com/pact-foundation/pact-reference/releases/download/libpact_mock_server_ffi-v0.0.15/libpact_mock_server_ffi-linux-x86_64.so.gz",
test: Installer{
os: linux,
Expand All @@ -33,7 +33,7 @@ func TestInstallerDownloader(t *testing.T) {
},
{
name: "mock server - osx x86",
pkg: mockServerPackage,
pkg: MockServerPackage,
want: "https://github.com/pact-foundation/pact-reference/releases/download/libpact_mock_server_ffi-v0.0.15/libpact_mock_server_ffi-osx-x86_64.dylib.gz",
test: Installer{
os: osx,
Expand All @@ -42,7 +42,7 @@ func TestInstallerDownloader(t *testing.T) {
},
{
name: "mock server - linux x86",
pkg: mockServerPackage,
pkg: MockServerPackage,
want: "https://github.com/pact-foundation/pact-reference/releases/download/libpact_mock_server_ffi-v0.0.15/libpact_mock_server_ffi-windows-x86_64.dll.gz",
test: Installer{
os: windows,
Expand Down Expand Up @@ -80,15 +80,15 @@ func TestInstallerDownloader(t *testing.T) {
defer func() { packages = oldPackages }()

packages = map[string]packageInfo{
verifierPackage: {
VerifierPackage: {
libName: "libpact_verifier_ffi",
version: "0.0.2",
semverRange: ">= 0.8.3, < 1.0.0",
testCommand: func() string {
return "0.8.0"
},
},
mockServerPackage: {
MockServerPackage: {
libName: "libpact_mock_server_ffi",
version: "0.0.15",
semverRange: ">= 0.0.15, < 1.0.0",
Expand Down
20 changes: 20 additions & 0 deletions v3/internal/checker/checker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package checker

import (
"github.com/pact-foundation/pact-go/v3/installer"
"github.com/pact-foundation/pact-go/v3/internal/native/mockserver"
"github.com/pact-foundation/pact-go/v3/internal/native/verifier"
)

func CheckInstall() error {
// initialised the lib registry
installer.LibRegistry[installer.MockServerPackage] = &mockserver.MockServer{}
installer.LibRegistry[installer.VerifierPackage] = &verifier.Verifier{}

i, err := installer.NewInstaller()
if err != nil {
return err
}

return i.CheckInstallation()
}

0 comments on commit bb17299

Please sign in to comment.