forked from jetstack/navigator
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Standalone API server with integration tests
* Tried to separate out the API server options that require kubernetes API configuration options. * Don't apply those command line options if `--standalone-mode` has been supplied. * Added a wrapper around the Kubernetes integration test framework to also launch the Navigator API server and connect it to the same Etcd server. * Currently parses stderr to check for a successful startup message, but the API server ought to have an `/healthz` endpoint. Fixes: jetstack#364
- Loading branch information
Showing
10 changed files
with
562 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package framework | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"net/url" | ||
"time" | ||
|
||
"sigs.k8s.io/testing_frameworks/integration" | ||
|
||
"github.com/jetstack/navigator/internal/test/integration/framework/internal" | ||
) | ||
|
||
type NavigatorAPIServer struct { | ||
URL *url.URL | ||
Path string | ||
Args []string | ||
StartTimeout time.Duration | ||
StopTimeout time.Duration | ||
CertDir string | ||
EtcdURL *url.URL | ||
APIServerURL *url.URL | ||
Out io.Writer | ||
Err io.Writer | ||
processState *internal.ProcessState | ||
} | ||
|
||
func (s *NavigatorAPIServer) Start() error { | ||
if s.EtcdURL == nil { | ||
return fmt.Errorf("expected EtcdURL to be configured") | ||
} | ||
|
||
var err error | ||
|
||
s.processState = &internal.ProcessState{} | ||
|
||
s.processState.DefaultedProcessInput, err = internal.DoDefaulting( | ||
"navigator-apiserver", | ||
s.URL, | ||
s.CertDir, | ||
s.Path, | ||
s.StartTimeout, | ||
s.StopTimeout, | ||
) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// s.processState.HealthCheckEndpoint = "/healthz" | ||
s.processState.StartMessage = "Serving securely on 127.0.0.1" | ||
s.URL = &s.processState.URL | ||
s.CertDir = s.processState.Dir | ||
s.Path = s.processState.Path | ||
s.StartTimeout = s.processState.StartTimeout | ||
s.StopTimeout = s.processState.StopTimeout | ||
|
||
s.processState.Args, err = internal.RenderTemplates( | ||
append( | ||
internal.DoAPIServerArgDefaulting(nil), | ||
s.Args..., | ||
), | ||
s, | ||
) | ||
if err != nil { | ||
return err | ||
} | ||
return s.processState.Start(s.Out, s.Err) | ||
} | ||
|
||
func (s *NavigatorAPIServer) Stop() error { | ||
return s.processState.Stop() | ||
} | ||
|
||
type NavigatorControlPlane struct { | ||
*integration.ControlPlane | ||
NavigatorAPIServer *NavigatorAPIServer | ||
} | ||
|
||
func (f *NavigatorControlPlane) Start() error { | ||
if f.ControlPlane == nil { | ||
f.ControlPlane = &integration.ControlPlane{} | ||
} | ||
err := f.ControlPlane.Start() | ||
if err != nil { | ||
return err | ||
} | ||
if f.NavigatorAPIServer == nil { | ||
f.NavigatorAPIServer = &NavigatorAPIServer{} | ||
} | ||
f.NavigatorAPIServer.EtcdURL = f.Etcd.URL | ||
f.NavigatorAPIServer.APIServerURL = f.APIServer.URL | ||
return f.NavigatorAPIServer.Start() | ||
} | ||
|
||
func (f *NavigatorControlPlane) Stop() error { | ||
if f.NavigatorAPIServer != nil { | ||
err := f.NavigatorAPIServer.Stop() | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
return f.ControlPlane.Stop() | ||
} | ||
|
||
func (f *NavigatorControlPlane) NavigatorAPIURL() *url.URL { | ||
return nil | ||
} |
53 changes: 53 additions & 0 deletions
53
internal/test/integration/framework/internal/address_manager.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package internal | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
) | ||
|
||
// AddressManager allocates a new address (interface & port) a process | ||
// can bind and keeps track of that. | ||
type AddressManager struct { | ||
port int | ||
host string | ||
} | ||
|
||
// Initialize returns a address a process can listen on. It returns | ||
// a tuple consisting of a free port and the hostname resolved to its IP. | ||
func (d *AddressManager) Initialize() (port int, resolvedHost string, err error) { | ||
if d.port != 0 { | ||
return 0, "", fmt.Errorf("this AddressManager is already initialized") | ||
} | ||
addr, err := net.ResolveTCPAddr("tcp", "localhost:0") | ||
if err != nil { | ||
return | ||
} | ||
l, err := net.ListenTCP("tcp", addr) | ||
if err != nil { | ||
return | ||
} | ||
d.port = l.Addr().(*net.TCPAddr).Port | ||
defer func() { | ||
err = l.Close() | ||
}() | ||
d.host = addr.IP.String() | ||
return d.port, d.host, nil | ||
} | ||
|
||
// Port returns the port that this AddressManager is managing. Port returns an | ||
// error if this AddressManager has not yet been initialized. | ||
func (d *AddressManager) Port() (int, error) { | ||
if d.port == 0 { | ||
return 0, fmt.Errorf("this AdressManager is not initialized yet") | ||
} | ||
return d.port, nil | ||
} | ||
|
||
// Host returns the host that this AddressManager is managing. Host returns an | ||
// error if this AddressManager has not yet been initialized. | ||
func (d *AddressManager) Host() (string, error) { | ||
if d.host == "" { | ||
return "", fmt.Errorf("this AdressManager is not initialized yet") | ||
} | ||
return d.host, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package internal | ||
|
||
var APIServerDefaultArgs = []string{ | ||
"--etcd-servers={{ if .EtcdURL }}{{ .EtcdURL.String }}{{ end }}", | ||
"--cert-dir={{ .CertDir }}", | ||
"--secure-port={{ if .URL }}{{ .URL.Port }}{{ end }}", | ||
"--bind-address={{ if .URL }}{{ .URL.Hostname }}{{ end }}", | ||
} | ||
|
||
func DoAPIServerArgDefaulting(args []string) []string { | ||
if len(args) != 0 { | ||
return args | ||
} | ||
|
||
return APIServerDefaultArgs | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package internal | ||
|
||
import ( | ||
"bytes" | ||
"html/template" | ||
) | ||
|
||
func RenderTemplates(argTemplates []string, data interface{}) (args []string, err error) { | ||
var t *template.Template | ||
|
||
for _, arg := range argTemplates { | ||
t, err = template.New(arg).Parse(arg) | ||
if err != nil { | ||
args = nil | ||
return | ||
} | ||
|
||
buf := &bytes.Buffer{} | ||
err = t.Execute(buf, data) | ||
if err != nil { | ||
args = nil | ||
return | ||
} | ||
args = append(args, buf.String()) | ||
} | ||
|
||
return | ||
} |
35 changes: 35 additions & 0 deletions
35
internal/test/integration/framework/internal/bin_path_finder.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package internal | ||
|
||
import ( | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
"runtime" | ||
"strings" | ||
) | ||
|
||
var assetsPath string | ||
|
||
func init() { | ||
_, thisFile, _, ok := runtime.Caller(0) | ||
if !ok { | ||
panic("Could not determine the path of the BinPathFinder") | ||
} | ||
assetsPath = filepath.Join(filepath.Dir(thisFile), "..", "assets", "bin") | ||
} | ||
|
||
// BinPathFinder checks the an environment variable, derived from the symbolic name, | ||
// and falls back to a default assets location when this variable is not set | ||
func BinPathFinder(symbolicName string) (binPath string) { | ||
punctuationPattern := regexp.MustCompile("[^A-Z0-9]+") | ||
sanitizedName := punctuationPattern.ReplaceAllString(strings.ToUpper(symbolicName), "_") | ||
leadingNumberPattern := regexp.MustCompile("^[0-9]+") | ||
sanitizedName = leadingNumberPattern.ReplaceAllString(sanitizedName, "") | ||
envVar := "TEST_ASSET_" + sanitizedName | ||
|
||
if val, ok := os.LookupEnv(envVar); ok { | ||
return val | ||
} | ||
|
||
return filepath.Join(assetsPath, symbolicName) | ||
} |
Oops, something went wrong.