Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
H
helm3
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
helm3
Commits
024f168b
Unverified
Commit
024f168b
authored
Oct 10, 2019
by
Matthew Fisher
Committed by
GitHub
Oct 10, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #6612 from bacongobbler/rebase-6167
feat(test): add `--logs` to `helm test`
parents
66e0c0b4
9b9dcebe
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
110 additions
and
4 deletions
+110
-4
tiller.proto
_proto/hapi/services/tiller.proto
+2
-0
release_testing.go
cmd/helm/release_testing.go
+3
-0
helm_test.md
docs/helm/helm_test.md
+2
-1
option.go
pkg/helm/option.go
+7
-0
client.go
pkg/kube/client.go
+16
-2
tiller.pb.go
pkg/proto/hapi/services/tiller.pb.go
+0
-0
environment.go
pkg/releasetesting/environment.go
+34
-1
environment_test.go
pkg/releasetesting/environment_test.go
+28
-0
environment.go
pkg/tiller/environment/environment.go
+7
-0
environment_test.go
pkg/tiller/environment/environment_test.go
+4
-0
release_server_test.go
pkg/tiller/release_server_test.go
+3
-0
release_testing.go
pkg/tiller/release_testing.go
+4
-0
No files found.
_proto/hapi/services/tiller.proto
View file @
024f168b
...
@@ -352,6 +352,8 @@ message TestReleaseRequest {
...
@@ -352,6 +352,8 @@ message TestReleaseRequest {
bool
parallel
=
4
;
bool
parallel
=
4
;
// maximum number of test pods to run in parallel
// maximum number of test pods to run in parallel
uint32
max_parallel
=
5
;
uint32
max_parallel
=
5
;
// logs specifies whether or not to dump the logs from the test pods
bool
logs
=
6
;
}
}
// TestReleaseResponse represents a message from executing a test
// TestReleaseResponse represents a message from executing a test
...
...
cmd/helm/release_testing.go
View file @
024f168b
...
@@ -41,6 +41,7 @@ type releaseTestCmd struct {
...
@@ -41,6 +41,7 @@ type releaseTestCmd struct {
cleanup
bool
cleanup
bool
parallel
bool
parallel
bool
maxParallel
uint32
maxParallel
uint32
logs
bool
}
}
func
newReleaseTestCmd
(
c
helm
.
Interface
,
out
io
.
Writer
)
*
cobra
.
Command
{
func
newReleaseTestCmd
(
c
helm
.
Interface
,
out
io
.
Writer
)
*
cobra
.
Command
{
...
@@ -71,6 +72,7 @@ func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command {
...
@@ -71,6 +72,7 @@ func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command {
f
.
BoolVar
(
&
rlsTest
.
cleanup
,
"cleanup"
,
false
,
"Delete test pods upon completion"
)
f
.
BoolVar
(
&
rlsTest
.
cleanup
,
"cleanup"
,
false
,
"Delete test pods upon completion"
)
f
.
BoolVar
(
&
rlsTest
.
parallel
,
"parallel"
,
false
,
"Run test pods in parallel"
)
f
.
BoolVar
(
&
rlsTest
.
parallel
,
"parallel"
,
false
,
"Run test pods in parallel"
)
f
.
Uint32Var
(
&
rlsTest
.
maxParallel
,
"max"
,
20
,
"Maximum number of test pods to run in parallel"
)
f
.
Uint32Var
(
&
rlsTest
.
maxParallel
,
"max"
,
20
,
"Maximum number of test pods to run in parallel"
)
f
.
BoolVar
(
&
rlsTest
.
logs
,
"logs"
,
false
,
"Dump the logs from test pods (this runs after all tests are complete, but before any cleanup"
)
// set defaults from environment
// set defaults from environment
settings
.
InitTLS
(
f
)
settings
.
InitTLS
(
f
)
...
@@ -85,6 +87,7 @@ func (t *releaseTestCmd) run() (err error) {
...
@@ -85,6 +87,7 @@ func (t *releaseTestCmd) run() (err error) {
helm
.
ReleaseTestCleanup
(
t
.
cleanup
),
helm
.
ReleaseTestCleanup
(
t
.
cleanup
),
helm
.
ReleaseTestParallel
(
t
.
parallel
),
helm
.
ReleaseTestParallel
(
t
.
parallel
),
helm
.
ReleaseTestMaxParallel
(
t
.
maxParallel
),
helm
.
ReleaseTestMaxParallel
(
t
.
maxParallel
),
helm
.
ReleaseTestLogs
(
t
.
logs
),
)
)
testErr
:=
&
testErr
{}
testErr
:=
&
testErr
{}
...
...
docs/helm/helm_test.md
View file @
024f168b
...
@@ -20,6 +20,7 @@ helm test [RELEASE] [flags]
...
@@ -20,6 +20,7 @@ helm test [RELEASE] [flags]
```
```
--cleanup Delete test pods upon completion
--cleanup Delete test pods upon completion
-h, --help help for test
-h, --help help for test
--logs Dump the logs from test pods (this runs after all tests are complete, but before any cleanup
--max uint32 Maximum number of test pods to run in parallel (default 20)
--max uint32 Maximum number of test pods to run in parallel (default 20)
--parallel Run test pods in parallel
--parallel Run test pods in parallel
--timeout int Time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks) (default 300)
--timeout int Time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks) (default 300)
...
@@ -47,4 +48,4 @@ helm test [RELEASE] [flags]
...
@@ -47,4 +48,4 @@ helm test [RELEASE] [flags]
*
[
helm
](
helm.md
)
- The Helm package manager for Kubernetes.
*
[
helm
](
helm.md
)
- The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on
25-Jul
-2019
###### Auto generated by spf13/cobra on
8-Oct
-2019
pkg/helm/option.go
View file @
024f168b
...
@@ -241,6 +241,13 @@ func ReleaseTestMaxParallel(max uint32) ReleaseTestOption {
...
@@ -241,6 +241,13 @@ func ReleaseTestMaxParallel(max uint32) ReleaseTestOption {
}
}
}
}
// ReleaseTestLogs is a boolean value representing whether to dump the logs from test pods
func
ReleaseTestLogs
(
logs
bool
)
ReleaseTestOption
{
return
func
(
opts
*
options
)
{
opts
.
testReq
.
Logs
=
logs
}
}
// RollbackTimeout specifies the number of seconds before kubernetes calls timeout
// RollbackTimeout specifies the number of seconds before kubernetes calls timeout
func
RollbackTimeout
(
timeout
int64
)
RollbackOption
{
func
RollbackTimeout
(
timeout
int64
)
RollbackOption
{
return
func
(
opts
*
options
)
{
return
func
(
opts
*
options
)
{
...
...
pkg/kube/client.go
View file @
024f168b
...
@@ -31,7 +31,7 @@ import (
...
@@ -31,7 +31,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/api/meta"
"github.com/evanphx/json-patch"
jsonpatch
"github.com/evanphx/json-patch"
appsv1
"k8s.io/api/apps/v1"
appsv1
"k8s.io/api/apps/v1"
appsv1beta1
"k8s.io/api/apps/v1beta1"
appsv1beta1
"k8s.io/api/apps/v1beta1"
appsv1beta2
"k8s.io/api/apps/v1beta2"
appsv1beta2
"k8s.io/api/apps/v1beta2"
...
@@ -935,7 +935,7 @@ func (c *Client) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader,
...
@@ -935,7 +935,7 @@ func (c *Client) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader,
}
}
func
(
c
*
Client
)
watchPodUntilComplete
(
timeout
time
.
Duration
,
info
*
resource
.
Info
)
error
{
func
(
c
*
Client
)
watchPodUntilComplete
(
timeout
time
.
Duration
,
info
*
resource
.
Info
)
error
{
lw
:=
cachetools
.
NewListWatchFromClient
(
info
.
Client
,
info
.
Mapping
.
Resource
.
Resource
,
info
.
Namespace
,
fields
.
Everything
(
))
lw
:=
cachetools
.
NewListWatchFromClient
(
info
.
Client
,
info
.
Mapping
.
Resource
.
Resource
,
info
.
Namespace
,
fields
.
ParseSelectorOrDie
(
fmt
.
Sprintf
(
"metadata.name=%s"
,
info
.
Name
)
))
c
.
Log
(
"Watching pod %s for completion with timeout of %v"
,
info
.
Name
,
timeout
)
c
.
Log
(
"Watching pod %s for completion with timeout of %v"
,
info
.
Name
,
timeout
)
ctx
,
cancel
:=
watchtools
.
ContextWithOptionalTimeout
(
context
.
Background
(),
timeout
)
ctx
,
cancel
:=
watchtools
.
ContextWithOptionalTimeout
(
context
.
Background
(),
timeout
)
...
@@ -947,6 +947,20 @@ func (c *Client) watchPodUntilComplete(timeout time.Duration, info *resource.Inf
...
@@ -947,6 +947,20 @@ func (c *Client) watchPodUntilComplete(timeout time.Duration, info *resource.Inf
return
err
return
err
}
}
// GetPodLogs takes pod name and namespace and returns the current logs (streaming is NOT enabled).
func
(
c
*
Client
)
GetPodLogs
(
name
,
ns
string
)
(
io
.
ReadCloser
,
error
)
{
client
,
err
:=
c
.
KubernetesClientSet
()
if
err
!=
nil
{
return
nil
,
err
}
req
:=
client
.
CoreV1
()
.
Pods
(
ns
)
.
GetLogs
(
name
,
&
v1
.
PodLogOptions
{})
logReader
,
err
:=
req
.
Stream
()
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"error in opening log stream, got: %s"
,
err
)
}
return
logReader
,
nil
}
func
isPodComplete
(
event
watch
.
Event
)
(
bool
,
error
)
{
func
isPodComplete
(
event
watch
.
Event
)
(
bool
,
error
)
{
o
,
ok
:=
event
.
Object
.
(
*
v1
.
Pod
)
o
,
ok
:=
event
.
Object
.
(
*
v1
.
Pod
)
if
!
ok
{
if
!
ok
{
...
...
pkg/proto/hapi/services/tiller.pb.go
View file @
024f168b
This diff is collapsed.
Click to expand it.
pkg/releasetesting/environment.go
View file @
024f168b
...
@@ -19,17 +19,22 @@ package releasetesting
...
@@ -19,17 +19,22 @@ package releasetesting
import
(
import
(
"bytes"
"bytes"
"fmt"
"fmt"
"io/ioutil"
"log"
"log"
"strings"
"sync"
"sync"
"time"
"time"
"k8s.io/api/core/v1"
v1
"k8s.io/api/core/v1"
"k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/proto/hapi/services"
"k8s.io/helm/pkg/proto/hapi/services"
"k8s.io/helm/pkg/tiller/environment"
"k8s.io/helm/pkg/tiller/environment"
)
)
// logEscaper is necessary for escaping control characters found in the log stream.
var
logEscaper
=
strings
.
NewReplacer
(
"%"
,
"%%"
)
// Environment encapsulates information about where test suite executes and returns results
// Environment encapsulates information about where test suite executes and returns results
type
Environment
struct
{
type
Environment
struct
{
Namespace
string
Namespace
string
...
@@ -126,3 +131,31 @@ func (env *Environment) DeleteTestPods(testManifests []string) {
...
@@ -126,3 +131,31 @@ func (env *Environment) DeleteTestPods(testManifests []string) {
}
}
}
}
}
}
// GetLogs collects the logs from the pods created in testManifests
func
(
env
*
Environment
)
GetLogs
(
testManifests
[]
string
)
{
for
_
,
testManifest
:=
range
testManifests
{
infos
,
err
:=
env
.
KubeClient
.
Build
(
env
.
Namespace
,
bytes
.
NewBufferString
(
testManifest
))
if
err
!=
nil
{
env
.
streamError
(
err
.
Error
())
continue
}
if
len
(
infos
)
==
0
{
env
.
streamError
(
fmt
.
Sprint
(
"Pod manifest is invalid. Unable to obtain the logs"
))
continue
}
podName
:=
infos
[
0
]
.
Object
.
(
*
v1
.
Pod
)
.
Name
lr
,
err
:=
env
.
KubeClient
.
GetPodLogs
(
podName
,
env
.
Namespace
)
if
err
!=
nil
{
env
.
streamError
(
err
.
Error
())
continue
}
logs
,
err
:=
ioutil
.
ReadAll
(
lr
)
if
err
!=
nil
{
env
.
streamError
(
err
.
Error
())
continue
}
msg
:=
fmt
.
Sprintf
(
"
\n
POD LOGS: %s
\n
%s"
,
podName
,
logEscaper
.
Replace
(
string
(
logs
)))
env
.
streamMessage
(
msg
,
release
.
TestRun_RUNNING
)
}
}
pkg/releasetesting/environment_test.go
View file @
024f168b
...
@@ -89,6 +89,24 @@ func TestDeleteTestPodsFailingDelete(t *testing.T) {
...
@@ -89,6 +89,24 @@ func TestDeleteTestPodsFailingDelete(t *testing.T) {
}
}
}
}
func
TestGetTestPodLogs
(
t
*
testing
.
T
)
{
mockTestSuite
:=
testSuiteFixture
([]
string
{
manifestWithTestSuccessHook
})
mockTestEnv
:=
newMockTestingEnvironment
()
mockTestEnv
.
KubeClient
=
newGetLogKubeClient
()
mockTestEnv
.
GetLogs
(
mockTestSuite
.
TestManifests
)
expectedMessage
:=
"ERROR: Pod manifest is invalid. Unable to obtain the logs"
stream
:=
mockTestEnv
.
Stream
.
(
*
mockStream
)
if
len
(
stream
.
messages
)
!=
1
{
t
.
Errorf
(
"Expected 1 message, got: %v"
,
len
(
stream
.
messages
))
}
if
stream
.
messages
[
0
]
.
Msg
!=
expectedMessage
{
t
.
Errorf
(
"Expected message '%s', got: %v"
,
expectedMessage
,
stream
.
messages
[
0
]
.
Msg
)
}
}
func
TestStreamMessage
(
t
*
testing
.
T
)
{
func
TestStreamMessage
(
t
*
testing
.
T
)
{
mockTestEnv
:=
newMockTestingEnvironment
()
mockTestEnv
:=
newMockTestingEnvironment
()
...
@@ -181,3 +199,13 @@ func newCreateFailingKubeClient() *createFailingKubeClient {
...
@@ -181,3 +199,13 @@ func newCreateFailingKubeClient() *createFailingKubeClient {
func
(
p
*
createFailingKubeClient
)
Create
(
ns
string
,
r
io
.
Reader
,
t
int64
,
shouldWait
bool
)
error
{
func
(
p
*
createFailingKubeClient
)
Create
(
ns
string
,
r
io
.
Reader
,
t
int64
,
shouldWait
bool
)
error
{
return
errors
.
New
(
"We ran out of budget and couldn't create finding-nemo"
)
return
errors
.
New
(
"We ran out of budget and couldn't create finding-nemo"
)
}
}
type
getLogKubeClient
struct
{
tillerEnv
.
PrintingKubeClient
}
func
newGetLogKubeClient
()
*
getLogKubeClient
{
return
&
getLogKubeClient
{
PrintingKubeClient
:
tillerEnv
.
PrintingKubeClient
{
Out
:
ioutil
.
Discard
},
}
}
pkg/tiller/environment/environment.go
View file @
024f168b
...
@@ -175,6 +175,8 @@ type KubeClient interface {
...
@@ -175,6 +175,8 @@ type KubeClient interface {
// and returns said phase (PodSucceeded or PodFailed qualify).
// and returns said phase (PodSucceeded or PodFailed qualify).
WaitAndGetCompletedPodPhase
(
namespace
string
,
reader
io
.
Reader
,
timeout
time
.
Duration
)
(
v1
.
PodPhase
,
error
)
WaitAndGetCompletedPodPhase
(
namespace
string
,
reader
io
.
Reader
,
timeout
time
.
Duration
)
(
v1
.
PodPhase
,
error
)
GetPodLogs
(
name
,
namespace
string
)
(
io
.
ReadCloser
,
error
)
WaitUntilCRDEstablished
(
reader
io
.
Reader
,
timeout
time
.
Duration
)
error
WaitUntilCRDEstablished
(
reader
io
.
Reader
,
timeout
time
.
Duration
)
error
}
}
...
@@ -255,6 +257,11 @@ func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(namespace string, reade
...
@@ -255,6 +257,11 @@ func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(namespace string, reade
return
v1
.
PodUnknown
,
err
return
v1
.
PodUnknown
,
err
}
}
// GetPodLogs implements KubeClient GetPodLogs.
func
(
p
*
PrintingKubeClient
)
GetPodLogs
(
name
,
ns
string
)
(
io
.
ReadCloser
,
error
)
{
return
nil
,
nil
}
// WaitUntilCRDEstablished implements KubeClient WaitUntilCRDEstablished.
// WaitUntilCRDEstablished implements KubeClient WaitUntilCRDEstablished.
func
(
p
*
PrintingKubeClient
)
WaitUntilCRDEstablished
(
reader
io
.
Reader
,
timeout
time
.
Duration
)
error
{
func
(
p
*
PrintingKubeClient
)
WaitUntilCRDEstablished
(
reader
io
.
Reader
,
timeout
time
.
Duration
)
error
{
_
,
err
:=
io
.
Copy
(
p
.
Out
,
reader
)
_
,
err
:=
io
.
Copy
(
p
.
Out
,
reader
)
...
...
pkg/tiller/environment/environment_test.go
View file @
024f168b
...
@@ -78,6 +78,10 @@ func (k *mockKubeClient) WaitAndGetCompletedPodStatus(namespace string, reader i
...
@@ -78,6 +78,10 @@ func (k *mockKubeClient) WaitAndGetCompletedPodStatus(namespace string, reader i
return
""
,
nil
return
""
,
nil
}
}
func
(
k
*
mockKubeClient
)
GetPodLogs
(
name
,
namespace
string
)
(
io
.
ReadCloser
,
error
)
{
return
nil
,
nil
}
func
(
k
*
mockKubeClient
)
WaitUntilCRDEstablished
(
reader
io
.
Reader
,
timeout
time
.
Duration
)
error
{
func
(
k
*
mockKubeClient
)
WaitUntilCRDEstablished
(
reader
io
.
Reader
,
timeout
time
.
Duration
)
error
{
return
nil
return
nil
}
}
...
...
pkg/tiller/release_server_test.go
View file @
024f168b
...
@@ -679,6 +679,9 @@ func (kc *mockHooksKubeClient) Validate(ns string, reader io.Reader) error {
...
@@ -679,6 +679,9 @@ func (kc *mockHooksKubeClient) Validate(ns string, reader io.Reader) error {
func
(
kc
*
mockHooksKubeClient
)
WaitAndGetCompletedPodPhase
(
namespace
string
,
reader
io
.
Reader
,
timeout
time
.
Duration
)
(
v1
.
PodPhase
,
error
)
{
func
(
kc
*
mockHooksKubeClient
)
WaitAndGetCompletedPodPhase
(
namespace
string
,
reader
io
.
Reader
,
timeout
time
.
Duration
)
(
v1
.
PodPhase
,
error
)
{
return
v1
.
PodUnknown
,
nil
return
v1
.
PodUnknown
,
nil
}
}
func
(
kc
*
mockHooksKubeClient
)
GetPodLogs
(
name
,
namespace
string
)
(
io
.
ReadCloser
,
error
)
{
return
nil
,
nil
}
func
(
kc
*
mockHooksKubeClient
)
WaitUntilCRDEstablished
(
reader
io
.
Reader
,
timeout
time
.
Duration
)
error
{
func
(
kc
*
mockHooksKubeClient
)
WaitUntilCRDEstablished
(
reader
io
.
Reader
,
timeout
time
.
Duration
)
error
{
return
nil
return
nil
...
...
pkg/tiller/release_testing.go
View file @
024f168b
...
@@ -69,6 +69,10 @@ func (s *ReleaseServer) RunReleaseTest(req *services.TestReleaseRequest, stream
...
@@ -69,6 +69,10 @@ func (s *ReleaseServer) RunReleaseTest(req *services.TestReleaseRequest, stream
Results
:
tSuite
.
Results
,
Results
:
tSuite
.
Results
,
}
}
if
req
.
Logs
{
testEnv
.
GetLogs
(
tSuite
.
TestManifests
)
}
if
req
.
Cleanup
{
if
req
.
Cleanup
{
testEnv
.
DeleteTestPods
(
tSuite
.
TestManifests
)
testEnv
.
DeleteTestPods
(
tSuite
.
TestManifests
)
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment