-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #311 from kool-dev/logs
Add kool deploy logs
- Loading branch information
Showing
10 changed files
with
578 additions
and
125 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package k8s | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"kool-dev/kool/api" | ||
"kool-dev/kool/cmd/builder" | ||
"kool-dev/kool/cmd/shell" | ||
"os" | ||
"path/filepath" | ||
) | ||
|
||
type K8S interface { | ||
Authenticate(string, string) (string, error) | ||
Kubectl(shell.PathChecker) (builder.Command, error) | ||
Cleanup(shell.OutputWritter) | ||
} | ||
|
||
type DefaultK8S struct { | ||
apiExec api.ExecCall | ||
resp *api.ExecResponse | ||
} | ||
|
||
var authTempPath = "/tmp" | ||
|
||
// NewDefaultK8S returns a new pointer for DefaultK8S with dependencies | ||
func NewDefaultK8S() *DefaultK8S { | ||
return &DefaultK8S{ | ||
apiExec: api.NewDefaultExecCall(), | ||
} | ||
} | ||
|
||
func (k *DefaultK8S) Authenticate(domain, service string) (cloudService string, err error) { | ||
k.apiExec.Body().Set("domain", domain) | ||
k.apiExec.Body().Set("service", service) | ||
|
||
if k.resp, err = k.apiExec.Call(); err != nil { | ||
return | ||
} | ||
|
||
if k.resp.Token == "" { | ||
err = fmt.Errorf("failed to generate access credentials to cloud deploy") | ||
return | ||
} | ||
|
||
cloudService = k.resp.Path | ||
|
||
err = os.WriteFile(k.getTempCAPath(), []byte(k.resp.CA), os.ModePerm) | ||
return | ||
} | ||
|
||
func (k *DefaultK8S) Kubectl(looker shell.PathChecker) (kube builder.Command, err error) { | ||
if k.resp == nil { | ||
err = errors.New("calling kubectl but did not authenticate") | ||
return | ||
} | ||
|
||
kube = builder.NewCommand("kubectl") | ||
|
||
kube.AppendArgs("--server", k.resp.Server) | ||
kube.AppendArgs("--token", k.resp.Token) | ||
kube.AppendArgs("--namespace", k.resp.Namespace) | ||
kube.AppendArgs("--certificate-authority", k.getTempCAPath()) | ||
|
||
if looker.LookPath(kube) != nil { | ||
// we do not have 'kubectl' on current path... let's use a container! | ||
kool := builder.NewCommand("kool") | ||
kool.AppendArgs( | ||
"docker", "--", | ||
"-v", fmt.Sprintf("%s:%s", k.getTempCAPath(), k.getTempCAPath()), | ||
"kooldev/toolkit:full", | ||
kube.Cmd(), | ||
) | ||
kool.AppendArgs(kube.Args()...) | ||
kube = kool | ||
} | ||
|
||
return | ||
} | ||
|
||
func (k *DefaultK8S) Cleanup(out shell.OutputWritter) { | ||
if err := os.Remove(k.getTempCAPath()); err != nil { | ||
out.Warning("failed to clear up temporary file; error:", err.Error()) | ||
} | ||
} | ||
|
||
func (k *DefaultK8S) getTempCAPath() string { | ||
return filepath.Join(authTempPath, ".kool-cluster-CA") | ||
} |
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,156 @@ | ||
package k8s | ||
|
||
import ( | ||
"errors" | ||
"kool-dev/kool/api" | ||
"kool-dev/kool/cmd/shell" | ||
"os" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
// fake api.ExecCall | ||
type fakeExecCall struct { | ||
api.DefaultEndpoint | ||
|
||
err error | ||
resp *api.ExecResponse | ||
} | ||
|
||
func (d *fakeExecCall) Call() (*api.ExecResponse, error) { | ||
return d.resp, d.err | ||
} | ||
|
||
func newFakeExecCall() *fakeExecCall { | ||
return &fakeExecCall{ | ||
DefaultEndpoint: *api.NewDefaultEndpoint(""), | ||
} | ||
} | ||
|
||
// fake shell.OutputWritter | ||
type fakeOutputWritter struct { | ||
warned []interface{} | ||
} | ||
|
||
func (*fakeOutputWritter) Println(args ...interface{}) { | ||
} | ||
|
||
func (*fakeOutputWritter) Printf(s string, args ...interface{}) { | ||
} | ||
|
||
func (f *fakeOutputWritter) Warning(args ...interface{}) { | ||
f.warned = append(f.warned, args...) | ||
} | ||
|
||
func (*fakeOutputWritter) Success(args ...interface{}) { | ||
} | ||
|
||
func TestNewDefaultK8S(t *testing.T) { | ||
k := NewDefaultK8S() | ||
if _, ok := k.apiExec.(*api.DefaultExecCall); !ok { | ||
t.Error("invalid type on apiExec") | ||
} | ||
} | ||
|
||
func TestAuthenticate(t *testing.T) { | ||
k := &DefaultK8S{ | ||
apiExec: newFakeExecCall(), | ||
} | ||
|
||
expectedErr := errors.New("call error") | ||
k.apiExec.(*fakeExecCall).err = expectedErr | ||
|
||
if _, err := k.Authenticate("foo", "bar"); !errors.Is(err, expectedErr) { | ||
t.Error("unexpected error return from Authenticate") | ||
} | ||
|
||
k.apiExec.(*fakeExecCall).err = nil | ||
k.apiExec.(*fakeExecCall).resp = &api.ExecResponse{ | ||
Server: "server", | ||
Namespace: "ns", | ||
Path: "path", | ||
Token: "", | ||
CA: "ca", | ||
} | ||
|
||
if _, err := k.Authenticate("foo", "bar"); !strings.Contains(err.Error(), "failed to generate access credentials") { | ||
t.Errorf("unexpected error from DeployExec call: %v", err) | ||
} | ||
|
||
k.apiExec.(*fakeExecCall).resp.Token = "token" | ||
authTempPath = t.TempDir() | ||
|
||
if cloudService, err := k.Authenticate("foo", "bar"); err != nil { | ||
t.Errorf("unexpected error from Authenticate call: %v", err) | ||
} else if cloudService != "path" { | ||
t.Errorf("unexpected cloudService return: %s", cloudService) | ||
} | ||
} | ||
|
||
func TestTempCAPath(t *testing.T) { | ||
k := NewDefaultK8S() | ||
|
||
authTempPath = "fake-path" | ||
|
||
if !strings.Contains(k.getTempCAPath(), authTempPath) { | ||
t.Error("missing authTempPath from temp CA path") | ||
} | ||
} | ||
|
||
func TestCleanup(t *testing.T) { | ||
k := NewDefaultK8S() | ||
|
||
authTempPath = t.TempDir() | ||
if err := os.WriteFile(k.getTempCAPath(), []byte("ca"), os.ModePerm); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
fakeOut := &fakeOutputWritter{} | ||
|
||
k.Cleanup(fakeOut) | ||
|
||
if len(fakeOut.warned) != 0 { | ||
t.Error("should not have warned on removing the file") | ||
} | ||
|
||
authTempPath = t.TempDir() + "test" | ||
k.Cleanup(fakeOut) | ||
|
||
if len(fakeOut.warned) != 2 { | ||
t.Error("should have warned on removing the file once") | ||
} | ||
} | ||
|
||
func TestKubectl(t *testing.T) { | ||
authTempPath = t.TempDir() | ||
|
||
k := &DefaultK8S{ | ||
apiExec: newFakeExecCall(), | ||
} | ||
|
||
k.apiExec.(*fakeExecCall).resp = &api.ExecResponse{ | ||
Server: "server", | ||
Namespace: "ns", | ||
Path: "path", | ||
Token: "token", | ||
CA: "ca", | ||
} | ||
|
||
fakeShell := &shell.FakeShell{} | ||
|
||
if _, err := k.Kubectl(fakeShell); !strings.Contains(err.Error(), "but did not auth") { | ||
t.Error("should get error before authenticating") | ||
} | ||
|
||
_, _ = k.Authenticate("foo", "bar") | ||
|
||
if cmd, _ := k.Kubectl(fakeShell); cmd.Cmd() != "kubectl" { | ||
t.Error("should use kubectl") | ||
} | ||
|
||
fakeShell.MockLookPath = errors.New("err") | ||
|
||
if cmd, _ := k.Kubectl(fakeShell); cmd.Cmd() != "kool" { | ||
t.Error("should use kool") | ||
} | ||
} |
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
Oops, something went wrong.