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
3cf8f2c8
Commit
3cf8f2c8
authored
Jul 14, 2017
by
Adam Reese
Committed by
GitHub
Jul 14, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2690 from adamreese/fix/helm-home-plugins-again-again
fix(helm): fix flag parsing once and for all
parents
1261f71f
7112a48a
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
284 additions
and
157 deletions
+284
-157
create_test.go
cmd/helm/create_test.go
+4
-5
dependency_build_test.go
cmd/helm/dependency_build_test.go
+4
-3
dependency_update_test.go
cmd/helm/dependency_update_test.go
+8
-8
fetch_test.go
cmd/helm/fetch_test.go
+4
-5
helm.go
cmd/helm/helm.go
+16
-45
helm_test.go
cmd/helm/helm_test.go
+16
-3
init.go
cmd/helm/init.go
+1
-1
init_test.go
cmd/helm/init_test.go
+4
-3
install.go
cmd/helm/install.go
+1
-1
load_plugins.go
cmd/helm/load_plugins.go
+2
-14
package_test.go
cmd/helm/package_test.go
+4
-3
plugin_test.go
cmd/helm/plugin_test.go
+10
-25
repo_add_test.go
cmd/helm/repo_add_test.go
+8
-6
repo_remove_test.go
cmd/helm/repo_remove_test.go
+4
-3
repo_update_test.go
cmd/helm/repo_update_test.go
+9
-6
reset.go
cmd/helm/reset.go
+1
-1
search_test.go
cmd/helm/search_test.go
+3
-2
environment.go
pkg/helm/environment/environment.go
+47
-14
environment_test.go
pkg/helm/environment/environment_test.go
+134
-0
plugin.go
pkg/plugin/plugin.go
+4
-5
environment.go
pkg/tiller/environment/environment.go
+0
-4
No files found.
cmd/helm/create_test.go
View file @
3cf8f2c8
...
@@ -23,8 +23,6 @@ import (
...
@@ -23,8 +23,6 @@ import (
"testing"
"testing"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/helm/environment"
"k8s.io/helm/pkg/helm/helmpath"
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/proto/hapi/chart"
)
)
...
@@ -87,13 +85,14 @@ func TestCreateStarterCmd(t *testing.T) {
...
@@ -87,13 +85,14 @@ func TestCreateStarterCmd(t *testing.T) {
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
old
:=
helmpath
.
Home
(
environment
.
DefaultHelmHome
)
cleanup
:=
resetEnv
()
settings
.
Home
=
thome
defer
func
()
{
defer
func
()
{
settings
.
Home
=
old
os
.
RemoveAll
(
thome
.
String
())
os
.
RemoveAll
(
thome
.
String
())
cleanup
()
}()
}()
settings
.
Home
=
thome
// Create a starter.
// Create a starter.
starterchart
:=
filepath
.
Join
(
thome
.
String
(),
"starters"
)
starterchart
:=
filepath
.
Join
(
thome
.
String
(),
"starters"
)
os
.
Mkdir
(
starterchart
,
0755
)
os
.
Mkdir
(
starterchart
,
0755
)
...
...
cmd/helm/dependency_build_test.go
View file @
3cf8f2c8
...
@@ -29,17 +29,18 @@ import (
...
@@ -29,17 +29,18 @@ import (
)
)
func
TestDependencyBuildCmd
(
t
*
testing
.
T
)
{
func
TestDependencyBuildCmd
(
t
*
testing
.
T
)
{
oldhome
:=
settings
.
Home
hh
,
err
:=
tempHelmHome
(
t
)
hh
,
err
:=
tempHelmHome
(
t
)
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
settings
.
Home
=
hh
cleanup
:=
resetEnv
()
defer
func
()
{
defer
func
()
{
os
.
RemoveAll
(
hh
.
String
())
os
.
RemoveAll
(
hh
.
String
())
settings
.
Home
=
oldhome
cleanup
()
}()
}()
settings
.
Home
=
hh
srv
:=
repotest
.
NewServer
(
hh
.
String
())
srv
:=
repotest
.
NewServer
(
hh
.
String
())
defer
srv
.
Stop
()
defer
srv
.
Stop
()
_
,
err
=
srv
.
CopyCharts
(
"testdata/testcharts/*.tgz"
)
_
,
err
=
srv
.
CopyCharts
(
"testdata/testcharts/*.tgz"
)
...
...
cmd/helm/dependency_update_test.go
View file @
3cf8f2c8
...
@@ -34,18 +34,18 @@ import (
...
@@ -34,18 +34,18 @@ import (
)
)
func
TestDependencyUpdateCmd
(
t
*
testing
.
T
)
{
func
TestDependencyUpdateCmd
(
t
*
testing
.
T
)
{
// Set up a testing helm home
oldhome
:=
settings
.
Home
hh
,
err
:=
tempHelmHome
(
t
)
hh
,
err
:=
tempHelmHome
(
t
)
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
settings
.
Home
=
hh
cleanup
:=
resetEnv
()
defer
func
()
{
defer
func
()
{
os
.
RemoveAll
(
hh
.
String
())
os
.
RemoveAll
(
hh
.
String
())
settings
.
Home
=
oldhome
cleanup
()
}()
}()
settings
.
Home
=
hh
srv
:=
repotest
.
NewServer
(
hh
.
String
())
srv
:=
repotest
.
NewServer
(
hh
.
String
())
defer
srv
.
Stop
()
defer
srv
.
Stop
()
copied
,
err
:=
srv
.
CopyCharts
(
"testdata/testcharts/*.tgz"
)
copied
,
err
:=
srv
.
CopyCharts
(
"testdata/testcharts/*.tgz"
)
...
@@ -129,18 +129,18 @@ func TestDependencyUpdateCmd(t *testing.T) {
...
@@ -129,18 +129,18 @@ func TestDependencyUpdateCmd(t *testing.T) {
}
}
func
TestDependencyUpdateCmd_SkipRefresh
(
t
*
testing
.
T
)
{
func
TestDependencyUpdateCmd_SkipRefresh
(
t
*
testing
.
T
)
{
// Set up a testing helm home
oldhome
:=
settings
.
Home
hh
,
err
:=
tempHelmHome
(
t
)
hh
,
err
:=
tempHelmHome
(
t
)
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
settings
.
Home
=
hh
cleanup
:=
resetEnv
()
defer
func
()
{
defer
func
()
{
os
.
RemoveAll
(
hh
.
String
())
os
.
RemoveAll
(
hh
.
String
())
settings
.
Home
=
oldhome
cleanup
()
}()
}()
settings
.
Home
=
hh
srv
:=
repotest
.
NewServer
(
hh
.
String
())
srv
:=
repotest
.
NewServer
(
hh
.
String
())
defer
srv
.
Stop
()
defer
srv
.
Stop
()
copied
,
err
:=
srv
.
CopyCharts
(
"testdata/testcharts/*.tgz"
)
copied
,
err
:=
srv
.
CopyCharts
(
"testdata/testcharts/*.tgz"
)
...
...
cmd/helm/fetch_test.go
View file @
3cf8f2c8
...
@@ -24,8 +24,6 @@ import (
...
@@ -24,8 +24,6 @@ import (
"regexp"
"regexp"
"testing"
"testing"
"k8s.io/helm/pkg/helm/environment"
"k8s.io/helm/pkg/helm/helmpath"
"k8s.io/helm/pkg/repo/repotest"
"k8s.io/helm/pkg/repo/repotest"
)
)
...
@@ -34,15 +32,16 @@ func TestFetchCmd(t *testing.T) {
...
@@ -34,15 +32,16 @@ func TestFetchCmd(t *testing.T) {
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
old
:=
helmpath
.
Home
(
environment
.
DefaultHelmHome
)
cleanup
:=
resetEnv
()
settings
.
Home
=
hh
defer
func
()
{
defer
func
()
{
settings
.
Home
=
old
os
.
RemoveAll
(
hh
.
String
())
os
.
RemoveAll
(
hh
.
String
())
cleanup
()
}()
}()
srv
:=
repotest
.
NewServer
(
hh
.
String
())
srv
:=
repotest
.
NewServer
(
hh
.
String
())
defer
srv
.
Stop
()
defer
srv
.
Stop
()
settings
.
Home
=
hh
// all flags will get "--home=TMDIR -d outdir" appended.
// all flags will get "--home=TMDIR -d outdir" appended.
tests
:=
[]
struct
{
tests
:=
[]
struct
{
name
string
name
string
...
...
cmd/helm/helm.go
View file @
3cf8f2c8
...
@@ -35,7 +35,6 @@ import (
...
@@ -35,7 +35,6 @@ import (
helm_env
"k8s.io/helm/pkg/helm/environment"
helm_env
"k8s.io/helm/pkg/helm/environment"
"k8s.io/helm/pkg/helm/portforwarder"
"k8s.io/helm/pkg/helm/portforwarder"
"k8s.io/helm/pkg/kube"
"k8s.io/helm/pkg/kube"
tiller_env
"k8s.io/helm/pkg/tiller/environment"
"k8s.io/helm/pkg/tlsutil"
"k8s.io/helm/pkg/tlsutil"
)
)
...
@@ -46,7 +45,6 @@ var (
...
@@ -46,7 +45,6 @@ var (
tlsVerify
bool
// enable TLS and verify remote certificates
tlsVerify
bool
// enable TLS and verify remote certificates
tlsEnable
bool
// enable TLS
tlsEnable
bool
// enable TLS
kubeContext
string
tillerTunnel
*
kube
.
Tunnel
tillerTunnel
*
kube
.
Tunnel
settings
helm_env
.
EnvSettings
settings
helm_env
.
EnvSettings
)
)
...
@@ -75,57 +73,25 @@ Environment:
...
@@ -75,57 +73,25 @@ Environment:
$KUBECONFIG set an alternative Kubernetes configuration file (default "~/.kube/config")
$KUBECONFIG set an alternative Kubernetes configuration file (default "~/.kube/config")
`
`
func
setFlagFromEnv
(
name
,
envar
string
,
cmd
*
cobra
.
Command
)
{
func
newRootCmd
(
args
[]
string
)
*
cobra
.
Command
{
if
cmd
.
Flags
()
.
Changed
(
name
)
{
return
}
if
v
,
ok
:=
os
.
LookupEnv
(
envar
);
ok
{
cmd
.
Flags
()
.
Set
(
name
,
v
)
}
}
func
setFlagsFromEnv
(
flags
map
[
string
]
string
,
cmd
*
cobra
.
Command
)
{
for
name
,
envar
:=
range
flags
{
setFlagFromEnv
(
name
,
envar
,
cmd
)
}
}
func
addRootFlags
(
cmd
*
cobra
.
Command
)
{
pf
:=
cmd
.
PersistentFlags
()
pf
.
StringVar
((
*
string
)(
&
settings
.
Home
),
"home"
,
helm_env
.
DefaultHelmHome
,
"location of your Helm config. Overrides $HELM_HOME"
)
pf
.
StringVar
(
&
settings
.
TillerHost
,
"host"
,
""
,
"address of Tiller. Overrides $HELM_HOST"
)
pf
.
StringVar
(
&
kubeContext
,
"kube-context"
,
""
,
"name of the kubeconfig context to use"
)
pf
.
BoolVar
(
&
settings
.
Debug
,
"debug"
,
false
,
"enable verbose output"
)
pf
.
StringVar
(
&
settings
.
TillerNamespace
,
"tiller-namespace"
,
tiller_env
.
DefaultTillerNamespace
,
"namespace of Tiller"
)
}
func
initRootFlags
(
cmd
*
cobra
.
Command
)
{
setFlagsFromEnv
(
map
[
string
]
string
{
"debug"
:
helm_env
.
DebugEnvVar
,
"home"
:
helm_env
.
HomeEnvVar
,
"host"
:
helm_env
.
HostEnvVar
,
"tiller-namespace"
:
tiller_env
.
TillerNamespaceEnvVar
,
},
cmd
.
Root
())
tlsCaCertFile
=
os
.
ExpandEnv
(
tlsCaCertFile
)
tlsCertFile
=
os
.
ExpandEnv
(
tlsCertFile
)
tlsKeyFile
=
os
.
ExpandEnv
(
tlsKeyFile
)
}
func
newRootCmd
()
*
cobra
.
Command
{
cmd
:=
&
cobra
.
Command
{
cmd
:=
&
cobra
.
Command
{
Use
:
"helm"
,
Use
:
"helm"
,
Short
:
"The Helm package manager for Kubernetes."
,
Short
:
"The Helm package manager for Kubernetes."
,
Long
:
globalUsage
,
Long
:
globalUsage
,
SilenceUsage
:
true
,
SilenceUsage
:
true
,
PersistentPreRun
:
func
(
cmd
*
cobra
.
Command
,
_
[]
string
)
{
PersistentPreRun
:
func
(
*
cobra
.
Command
,
[]
string
)
{
initRootFlags
(
cmd
)
tlsCaCertFile
=
os
.
ExpandEnv
(
tlsCaCertFile
)
tlsCertFile
=
os
.
ExpandEnv
(
tlsCertFile
)
tlsKeyFile
=
os
.
ExpandEnv
(
tlsKeyFile
)
},
},
PersistentPostRun
:
func
(
*
cobra
.
Command
,
[]
string
)
{
PersistentPostRun
:
func
(
*
cobra
.
Command
,
[]
string
)
{
teardown
()
teardown
()
},
},
}
}
addRootFlags
(
cmd
)
flags
:=
cmd
.
PersistentFlags
()
settings
.
AddFlags
(
flags
)
out
:=
cmd
.
OutOrStdout
()
out
:=
cmd
.
OutOrStdout
()
cmd
.
AddCommand
(
cmd
.
AddCommand
(
...
@@ -167,6 +133,11 @@ func newRootCmd() *cobra.Command {
...
@@ -167,6 +133,11 @@ func newRootCmd() *cobra.Command {
markDeprecated
(
newRepoUpdateCmd
(
out
),
"use 'helm repo update'
\n
"
),
markDeprecated
(
newRepoUpdateCmd
(
out
),
"use 'helm repo update'
\n
"
),
)
)
flags
.
Parse
(
args
)
// set defaults from environment
settings
.
Init
(
flags
)
// Find and add plugins
// Find and add plugins
loadPlugins
(
cmd
,
out
)
loadPlugins
(
cmd
,
out
)
...
@@ -179,7 +150,7 @@ func init() {
...
@@ -179,7 +150,7 @@ func init() {
}
}
func
main
()
{
func
main
()
{
cmd
:=
newRootCmd
()
cmd
:=
newRootCmd
(
os
.
Args
[
1
:
]
)
if
err
:=
cmd
.
Execute
();
err
!=
nil
{
if
err
:=
cmd
.
Execute
();
err
!=
nil
{
os
.
Exit
(
1
)
os
.
Exit
(
1
)
}
}
...
@@ -192,7 +163,7 @@ func markDeprecated(cmd *cobra.Command, notice string) *cobra.Command {
...
@@ -192,7 +163,7 @@ func markDeprecated(cmd *cobra.Command, notice string) *cobra.Command {
func
setupConnection
(
c
*
cobra
.
Command
,
args
[]
string
)
error
{
func
setupConnection
(
c
*
cobra
.
Command
,
args
[]
string
)
error
{
if
settings
.
TillerHost
==
""
{
if
settings
.
TillerHost
==
""
{
config
,
client
,
err
:=
getKubeClient
(
k
ubeContext
)
config
,
client
,
err
:=
getKubeClient
(
settings
.
K
ubeContext
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
...
...
cmd/helm/helm_test.go
View file @
3cf8f2c8
...
@@ -25,6 +25,7 @@ import (
...
@@ -25,6 +25,7 @@ import (
"os"
"os"
"path/filepath"
"path/filepath"
"regexp"
"regexp"
"strings"
"testing"
"testing"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/golang/protobuf/ptypes/timestamp"
...
@@ -232,8 +233,8 @@ func ensureTestHome(home helmpath.Home, t *testing.T) error {
...
@@ -232,8 +233,8 @@ func ensureTestHome(home helmpath.Home, t *testing.T) error {
}
}
func
TestRootCmd
(
t
*
testing
.
T
)
{
func
TestRootCmd
(
t
*
testing
.
T
)
{
oldhome
:=
os
.
Getenv
(
"HELM_HOME"
)
cleanup
:=
resetEnv
(
)
defer
os
.
Setenv
(
"HELM_HOME"
,
oldhome
)
defer
cleanup
(
)
tests
:=
[]
struct
{
tests
:=
[]
struct
{
name
string
name
string
...
@@ -287,7 +288,7 @@ func TestRootCmd(t *testing.T) {
...
@@ -287,7 +288,7 @@ func TestRootCmd(t *testing.T) {
os
.
Setenv
(
k
,
v
)
os
.
Setenv
(
k
,
v
)
}
}
cmd
:=
newRootCmd
()
cmd
:=
newRootCmd
(
tt
.
args
)
cmd
.
SetOutput
(
ioutil
.
Discard
)
cmd
.
SetOutput
(
ioutil
.
Discard
)
cmd
.
SetArgs
(
tt
.
args
)
cmd
.
SetArgs
(
tt
.
args
)
cmd
.
Run
=
func
(
*
cobra
.
Command
,
[]
string
)
{}
cmd
.
Run
=
func
(
*
cobra
.
Command
,
[]
string
)
{}
...
@@ -306,3 +307,15 @@ func TestRootCmd(t *testing.T) {
...
@@ -306,3 +307,15 @@ func TestRootCmd(t *testing.T) {
})
})
}
}
}
}
func
resetEnv
()
func
()
{
origSettings
:=
settings
origEnv
:=
os
.
Environ
()
return
func
()
{
settings
=
origSettings
for
_
,
pair
:=
range
origEnv
{
kv
:=
strings
.
SplitN
(
pair
,
"="
,
2
)
os
.
Setenv
(
kv
[
0
],
kv
[
1
])
}
}
}
cmd/helm/init.go
View file @
3cf8f2c8
...
@@ -230,7 +230,7 @@ func (i *initCmd) run() error {
...
@@ -230,7 +230,7 @@ func (i *initCmd) run() error {
if
!
i
.
clientOnly
{
if
!
i
.
clientOnly
{
if
i
.
kubeClient
==
nil
{
if
i
.
kubeClient
==
nil
{
_
,
c
,
err
:=
getKubeClient
(
k
ubeContext
)
_
,
c
,
err
:=
getKubeClient
(
settings
.
K
ubeContext
)
if
err
!=
nil
{
if
err
!=
nil
{
return
fmt
.
Errorf
(
"could not get kubernetes client: %s"
,
err
)
return
fmt
.
Errorf
(
"could not get kubernetes client: %s"
,
err
)
}
}
...
...
cmd/helm/init_test.go
View file @
3cf8f2c8
...
@@ -140,13 +140,14 @@ func TestInitCmd_dryRun(t *testing.T) {
...
@@ -140,13 +140,14 @@ func TestInitCmd_dryRun(t *testing.T) {
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
dbg
:=
settings
.
Debug
cleanup
:=
resetEnv
()
settings
.
Debug
=
true
defer
func
()
{
defer
func
()
{
os
.
Remove
(
home
)
os
.
Remove
(
home
)
settings
.
Debug
=
dbg
cleanup
()
}()
}()
settings
.
Debug
=
true
var
buf
bytes
.
Buffer
var
buf
bytes
.
Buffer
fc
:=
fake
.
NewSimpleClientset
()
fc
:=
fake
.
NewSimpleClientset
()
cmd
:=
&
initCmd
{
cmd
:=
&
initCmd
{
...
...
cmd/helm/install.go
View file @
3cf8f2c8
...
@@ -434,7 +434,7 @@ func generateName(nameTemplate string) (string, error) {
...
@@ -434,7 +434,7 @@ func generateName(nameTemplate string) (string, error) {
}
}
func
defaultNamespace
()
string
{
func
defaultNamespace
()
string
{
if
ns
,
_
,
err
:=
kube
.
GetConfig
(
k
ubeContext
)
.
Namespace
();
err
==
nil
{
if
ns
,
_
,
err
:=
kube
.
GetConfig
(
settings
.
K
ubeContext
)
.
Namespace
();
err
==
nil
{
return
ns
return
ns
}
}
return
"default"
return
"default"
...
...
cmd/helm/load_plugins.go
View file @
3cf8f2c8
...
@@ -24,9 +24,7 @@ import (
...
@@ -24,9 +24,7 @@ import (
"strings"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
helm_env
"k8s.io/helm/pkg/helm/environment"
"k8s.io/helm/pkg/plugin"
"k8s.io/helm/pkg/plugin"
)
)
...
@@ -38,20 +36,11 @@ import (
...
@@ -38,20 +36,11 @@ import (
func
loadPlugins
(
baseCmd
*
cobra
.
Command
,
out
io
.
Writer
)
{
func
loadPlugins
(
baseCmd
*
cobra
.
Command
,
out
io
.
Writer
)
{
// If HELM_NO_PLUGINS is set to 1, do not load plugins.
// If HELM_NO_PLUGINS is set to 1, do not load plugins.
if
os
.
Getenv
(
helm_env
.
PluginDisableEnvVar
)
==
"1"
{
if
os
.
Getenv
(
"HELM_NO_PLUGINS"
)
==
"1"
{
return
return
}
}
// manually handel processing of HELM_HOME and --home
// debug("HELM_PLUGIN_DIRS=%s", settings.PluginDirs())
helmHome
:=
"$HOME/.helm"
if
h
,
ok
:=
os
.
LookupEnv
(
"HELM_HOME"
);
ok
{
helmHome
=
h
}
fs
:=
pflag
.
NewFlagSet
(
"homer"
,
pflag
.
ContinueOnError
)
fs
.
StringVar
((
*
string
)(
&
settings
.
Home
),
"home"
,
helmHome
,
"location of your Helm config. Overrides $HELM_HOME"
)
fs
.
Parse
(
os
.
Args
)
found
,
err
:=
findPlugins
(
settings
.
PluginDirs
())
found
,
err
:=
findPlugins
(
settings
.
PluginDirs
())
if
err
!=
nil
{
if
err
!=
nil
{
fmt
.
Fprintf
(
os
.
Stderr
,
"failed to load plugins: %s"
,
err
)
fmt
.
Fprintf
(
os
.
Stderr
,
"failed to load plugins: %s"
,
err
)
...
@@ -63,7 +52,6 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
...
@@ -63,7 +52,6 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
if
err
:=
cmd
.
Parent
()
.
ParseFlags
(
k
);
err
!=
nil
{
if
err
:=
cmd
.
Parent
()
.
ParseFlags
(
k
);
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
initRootFlags
(
cmd
)
return
u
,
nil
return
u
,
nil
}
}
...
...
cmd/helm/package_test.go
View file @
3cf8f2c8
...
@@ -143,14 +143,15 @@ func TestPackage(t *testing.T) {
...
@@ -143,14 +143,15 @@ func TestPackage(t *testing.T) {
}
}
ensureTestHome
(
helmpath
.
Home
(
tmp
),
t
)
ensureTestHome
(
helmpath
.
Home
(
tmp
),
t
)
oldhome
:=
settings
.
Home
cleanup
:=
resetEnv
()
settings
.
Home
=
helmpath
.
Home
(
tmp
)
defer
func
()
{
defer
func
()
{
settings
.
Home
=
oldhome
os
.
Chdir
(
origDir
)
os
.
Chdir
(
origDir
)
os
.
RemoveAll
(
tmp
)
os
.
RemoveAll
(
tmp
)
cleanup
()
}()
}()
settings
.
Home
=
helmpath
.
Home
(
tmp
)
for
_
,
tt
:=
range
tests
{
for
_
,
tt
:=
range
tests
{
buf
:=
bytes
.
NewBuffer
(
nil
)
buf
:=
bytes
.
NewBuffer
(
nil
)
c
:=
newPackageCmd
(
buf
)
c
:=
newPackageCmd
(
buf
)
...
...
cmd/helm/plugin_test.go
View file @
3cf8f2c8
...
@@ -23,7 +23,6 @@ import (
...
@@ -23,7 +23,6 @@ import (
"strings"
"strings"
"testing"
"testing"
helm_env
"k8s.io/helm/pkg/helm/environment"
"k8s.io/helm/pkg/helm/helmpath"
"k8s.io/helm/pkg/helm/helmpath"
"k8s.io/helm/pkg/plugin"
"k8s.io/helm/pkg/plugin"
...
@@ -64,25 +63,13 @@ func TestManuallyProcessArgs(t *testing.T) {
...
@@ -64,25 +63,13 @@ func TestManuallyProcessArgs(t *testing.T) {
}
}
// resetEnv sets an env var, and returns a defer function to reset the env
func
resetEnv
(
name
,
val
string
)
func
()
{
original
,
ok
:=
os
.
LookupEnv
(
name
)
os
.
Setenv
(
name
,
val
)
if
ok
{
return
func
()
{
os
.
Setenv
(
name
,
original
)
}
}
return
func
()
{
os
.
Unsetenv
(
name
)
}
}
func
TestLoadPlugins
(
t
*
testing
.
T
)
{
func
TestLoadPlugins
(
t
*
testing
.
T
)
{
// Set helm home to point to testdata
cleanup
:=
resetEnv
()
old
:=
settings
.
Home
defer
cleanup
()
settings
.
Home
=
"testdata/helmhome"
settings
.
Home
=
"testdata/helmhome"
cleanup
:=
resetEnv
(
"HELM_HOME"
,
settings
.
Home
.
String
())
defer
func
()
{
os
.
Setenv
(
"HELM_HOME"
,
settings
.
Home
.
String
())
settings
.
Home
=
old
cleanup
()
}()
hh
:=
settings
.
Home
hh
:=
settings
.
Home
out
:=
bytes
.
NewBuffer
(
nil
)
out
:=
bytes
.
NewBuffer
(
nil
)
...
@@ -149,14 +136,12 @@ func TestLoadPlugins(t *testing.T) {
...
@@ -149,14 +136,12 @@ func TestLoadPlugins(t *testing.T) {
}
}
func
TestLoadPlugins_HelmNoPlugins
(
t
*
testing
.
T
)
{
func
TestLoadPlugins_HelmNoPlugins
(
t
*
testing
.
T
)
{
// Set helm home to point to testdata
cleanup
:=
resetEnv
()
old
:=
settings
.
Home
defer
cleanup
()
settings
.
Home
=
"testdata/helmhome"
settings
.
Home
=
"testdata/helmhome"
os
.
Setenv
(
helm_env
.
PluginDisableEnvVar
,
"1"
)
defer
func
()
{
os
.
Setenv
(
"HELM_NO_PLUGINS"
,
"1"
)
settings
.
Home
=
old
os
.
Unsetenv
(
helm_env
.
PluginDisableEnvVar
)
}()
out
:=
bytes
.
NewBuffer
(
nil
)
out
:=
bytes
.
NewBuffer
(
nil
)
cmd
:=
&
cobra
.
Command
{}
cmd
:=
&
cobra
.
Command
{}
...
...
cmd/helm/repo_add_test.go
View file @
3cf8f2c8
...
@@ -33,17 +33,18 @@ func TestRepoAddCmd(t *testing.T) {
...
@@ -33,17 +33,18 @@ func TestRepoAddCmd(t *testing.T) {
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
oldhome
:=
settings
.
Home
cleanup
:=
resetEnv
()
settings
.
Home
=
thome
defer
func
()
{
defer
func
()
{
srv
.
Stop
()
srv
.
Stop
()
settings
.
Home
=
oldhome
os
.
Remove
(
thome
.
String
())
os
.
Remove
(
thome
.
String
())
cleanup
()
}()
}()
if
err
:=
ensureTestHome
(
thome
,
t
);
err
!=
nil
{
if
err
:=
ensureTestHome
(
thome
,
t
);
err
!=
nil
{
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
settings
.
Home
=
thome
tests
:=
[]
releaseCase
{
tests
:=
[]
releaseCase
{
{
{
name
:
"add a repository"
,
name
:
"add a repository"
,
...
@@ -67,18 +68,19 @@ func TestRepoAdd(t *testing.T) {
...
@@ -67,18 +68,19 @@ func TestRepoAdd(t *testing.T) {
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
oldhome
:=
settings
.
Home
cleanup
:=
resetEnv
()
settings
.
Home
=
thome
hh
:=
thome
hh
:=
thome
defer
func
()
{
defer
func
()
{
ts
.
Stop
()
ts
.
Stop
()
settings
.
Home
=
oldhome
os
.
Remove
(
thome
.
String
())
os
.
Remove
(
thome
.
String
())
cleanup
()
}()
}()
if
err
:=
ensureTestHome
(
hh
,
t
);
err
!=
nil
{
if
err
:=
ensureTestHome
(
hh
,
t
);
err
!=
nil
{
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
settings
.
Home
=
thome
if
err
:=
addRepository
(
testName
,
ts
.
URL
(),
hh
,
""
,
""
,
""
,
true
);
err
!=
nil
{
if
err
:=
addRepository
(
testName
,
ts
.
URL
(),
hh
,
""
,
""
,
""
,
true
);
err
!=
nil
{
t
.
Error
(
err
)
t
.
Error
(
err
)
}
}
...
...
cmd/helm/repo_remove_test.go
View file @
3cf8f2c8
...
@@ -33,18 +33,19 @@ func TestRepoRemove(t *testing.T) {
...
@@ -33,18 +33,19 @@ func TestRepoRemove(t *testing.T) {
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
oldhome
:=
settings
.
Home
settings
.
Home
=
thome
hh
:=
helmpath
.
Home
(
thome
)
hh
:=
helmpath
.
Home
(
thome
)
cleanup
:=
resetEnv
()
defer
func
()
{
defer
func
()
{
ts
.
Stop
()
ts
.
Stop
()
settings
.
Home
=
oldhome
os
.
Remove
(
thome
.
String
())
os
.
Remove
(
thome
.
String
())
cleanup
()
}()
}()
if
err
:=
ensureTestHome
(
hh
,
t
);
err
!=
nil
{
if
err
:=
ensureTestHome
(
hh
,
t
);
err
!=
nil
{
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
settings
.
Home
=
thome
b
:=
bytes
.
NewBuffer
(
nil
)
b
:=
bytes
.
NewBuffer
(
nil
)
if
err
:=
removeRepoLine
(
b
,
testName
,
hh
);
err
==
nil
{
if
err
:=
removeRepoLine
(
b
,
testName
,
hh
);
err
==
nil
{
...
...
cmd/helm/repo_update_test.go
View file @
3cf8f2c8
...
@@ -34,13 +34,15 @@ func TestUpdateCmd(t *testing.T) {
...
@@ -34,13 +34,15 @@ func TestUpdateCmd(t *testing.T) {
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
oldhome
:=
settings
.
Home
settings
.
Home
=
thome
cleanup
:=
resetEnv
()
defer
func
()
{
defer
func
()
{
settings
.
Home
=
oldhome
os
.
Remove
(
thome
.
String
())
os
.
Remove
(
thome
.
String
())
cleanup
()
}()
}()
settings
.
Home
=
thome
out
:=
bytes
.
NewBuffer
(
nil
)
out
:=
bytes
.
NewBuffer
(
nil
)
// Instead of using the HTTP updater, we provide our own for this test.
// Instead of using the HTTP updater, we provide our own for this test.
// The TestUpdateCharts test verifies the HTTP behavior independently.
// The TestUpdateCharts test verifies the HTTP behavior independently.
...
@@ -69,18 +71,19 @@ func TestUpdateCharts(t *testing.T) {
...
@@ -69,18 +71,19 @@ func TestUpdateCharts(t *testing.T) {
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
oldhome
:=
settings
.
Home
settings
.
Home
=
thome
hh
:=
helmpath
.
Home
(
thome
)
hh
:=
helmpath
.
Home
(
thome
)
cleanup
:=
resetEnv
()
defer
func
()
{
defer
func
()
{
ts
.
Stop
()
ts
.
Stop
()
settings
.
Home
=
oldhome
os
.
Remove
(
thome
.
String
())
os
.
Remove
(
thome
.
String
())
cleanup
()
}()
}()
if
err
:=
ensureTestHome
(
hh
,
t
);
err
!=
nil
{
if
err
:=
ensureTestHome
(
hh
,
t
);
err
!=
nil
{
t
.
Fatal
(
err
)
t
.
Fatal
(
err
)
}
}
settings
.
Home
=
thome
r
,
err
:=
repo
.
NewChartRepository
(
&
repo
.
Entry
{
r
,
err
:=
repo
.
NewChartRepository
(
&
repo
.
Entry
{
Name
:
"charts"
,
Name
:
"charts"
,
URL
:
ts
.
URL
(),
URL
:
ts
.
URL
(),
...
...
cmd/helm/reset.go
View file @
3cf8f2c8
...
@@ -86,7 +86,7 @@ func newResetCmd(client helm.Interface, out io.Writer) *cobra.Command {
...
@@ -86,7 +86,7 @@ func newResetCmd(client helm.Interface, out io.Writer) *cobra.Command {
// runReset uninstalls tiller from Kubernetes Cluster and deletes local config
// runReset uninstalls tiller from Kubernetes Cluster and deletes local config
func
(
d
*
resetCmd
)
run
()
error
{
func
(
d
*
resetCmd
)
run
()
error
{
if
d
.
kubeClient
==
nil
{
if
d
.
kubeClient
==
nil
{
_
,
c
,
err
:=
getInternalKubeClient
(
k
ubeContext
)
_
,
c
,
err
:=
getInternalKubeClient
(
settings
.
K
ubeContext
)
if
err
!=
nil
{
if
err
!=
nil
{
return
fmt
.
Errorf
(
"could not get kubernetes client: %s"
,
err
)
return
fmt
.
Errorf
(
"could not get kubernetes client: %s"
,
err
)
}
}
...
...
cmd/helm/search_test.go
View file @
3cf8f2c8
...
@@ -68,9 +68,10 @@ func TestSearchCmd(t *testing.T) {
...
@@ -68,9 +68,10 @@ func TestSearchCmd(t *testing.T) {
},
},
}
}
oldhome
:=
settings
.
Home
cleanup
:=
resetEnv
()
defer
cleanup
()
settings
.
Home
=
"testdata/helmhome"
settings
.
Home
=
"testdata/helmhome"
defer
func
()
{
settings
.
Home
=
oldhome
}()
for
_
,
tt
:=
range
tests
{
for
_
,
tt
:=
range
tests
{
buf
:=
bytes
.
NewBuffer
(
nil
)
buf
:=
bytes
.
NewBuffer
(
nil
)
...
...
pkg/helm/environment/environment.go
View file @
3cf8f2c8
...
@@ -26,20 +26,9 @@ import (
...
@@ -26,20 +26,9 @@ import (
"os"
"os"
"path/filepath"
"path/filepath"
"k8s.io/helm/pkg/helm/helmpath"
"github.com/spf13/pflag"
)
const
(
"k8s.io/helm/pkg/helm/helmpath"
// HomeEnvVar is the HELM_HOME environment variable key.
HomeEnvVar
=
"HELM_HOME"
// PluginEnvVar is the HELM_PLUGIN environment variable key.
PluginEnvVar
=
"HELM_PLUGIN"
// PluginDisableEnvVar is the HELM_NO_PLUGINS environment variable key.
PluginDisableEnvVar
=
"HELM_NO_PLUGINS"
// HostEnvVar is the HELM_HOST environment variable key.
HostEnvVar
=
"HELM_HOST"
// DebugEnvVar is the HELM_DEBUG environment variable key.
DebugEnvVar
=
"HELM_DEBUG"
)
)
// DefaultHelmHome is the default HELM_HOME.
// DefaultHelmHome is the default HELM_HOME.
...
@@ -55,12 +44,56 @@ type EnvSettings struct {
...
@@ -55,12 +44,56 @@ type EnvSettings struct {
Home
helmpath
.
Home
Home
helmpath
.
Home
// Debug indicates whether or not Helm is running in Debug mode.
// Debug indicates whether or not Helm is running in Debug mode.
Debug
bool
Debug
bool
// KubeContext is the name of the kubeconfig context.
KubeContext
string
}
// AddFlags binds flags to the given flagset.
func
(
s
*
EnvSettings
)
AddFlags
(
fs
*
pflag
.
FlagSet
)
{
fs
.
StringVar
((
*
string
)(
&
s
.
Home
),
"home"
,
DefaultHelmHome
,
"location of your Helm config. Overrides $HELM_HOME"
)
fs
.
StringVar
(
&
s
.
TillerHost
,
"host"
,
""
,
"address of Tiller. Overrides $HELM_HOST"
)
fs
.
StringVar
(
&
s
.
KubeContext
,
"kube-context"
,
""
,
"name of the kubeconfig context to use"
)
fs
.
BoolVar
(
&
s
.
Debug
,
"debug"
,
false
,
"enable verbose output"
)
fs
.
StringVar
(
&
s
.
TillerNamespace
,
"tiller-namespace"
,
"kube-system"
,
"namespace of Tiller"
)
}
// Init sets values from the environment.
func
(
s
*
EnvSettings
)
Init
(
fs
*
pflag
.
FlagSet
)
{
for
name
,
envar
:=
range
envMap
{
setFlagFromEnv
(
name
,
envar
,
fs
)
}
}
}
// PluginDirs is the path to the plugin directories.
// PluginDirs is the path to the plugin directories.
func
(
s
EnvSettings
)
PluginDirs
()
string
{
func
(
s
EnvSettings
)
PluginDirs
()
string
{
if
d
:=
os
.
Getenv
(
PluginEnvVar
);
d
!=
""
{
if
d
,
ok
:=
os
.
LookupEnv
(
"HELM_PLUGIN"
);
ok
{
return
d
return
d
}
}
return
s
.
Home
.
Plugins
()
return
s
.
Home
.
Plugins
()
}
}
// envMap maps flag names to envvars
var
envMap
=
map
[
string
]
string
{
"debug"
:
"HELM_DEBUG"
,
"home"
:
"HELM_HOME"
,
"host"
:
"HELM_HOST"
,
"tiller-namespace"
:
"TILLER_NAMESPACE"
,
}
func
setFlagFromEnv
(
name
,
envar
string
,
fs
*
pflag
.
FlagSet
)
{
if
fs
.
Changed
(
name
)
{
return
}
if
v
,
ok
:=
os
.
LookupEnv
(
envar
);
ok
{
fs
.
Set
(
name
,
v
)
}
}
// Deprecated
const
(
HomeEnvVar
=
"HELM_HOME"
PluginEnvVar
=
"HELM_PLUGIN"
PluginDisableEnvVar
=
"HELM_NO_PLUGINS"
HostEnvVar
=
"HELM_HOST"
DebugEnvVar
=
"HELM_DEBUG"
)
pkg/helm/environment/environment_test.go
0 → 100644
View file @
3cf8f2c8
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
environment
import
(
"os"
"strings"
"testing"
"k8s.io/helm/pkg/helm/helmpath"
"github.com/spf13/pflag"
)
func
TestEnvSettings
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
// input
args
[]
string
envars
map
[
string
]
string
// expected values
home
,
host
,
ns
,
kcontext
,
plugins
string
debug
bool
}{
{
name
:
"defaults"
,
args
:
[]
string
{},
home
:
DefaultHelmHome
,
plugins
:
helmpath
.
Home
(
DefaultHelmHome
)
.
Plugins
(),
ns
:
"kube-system"
,
},
{
name
:
"with flags set"
,
args
:
[]
string
{
"--home"
,
"/foo"
,
"--host=here"
,
"--debug"
,
"--tiller-namespace=myns"
},
home
:
"/foo"
,
plugins
:
helmpath
.
Home
(
"/foo"
)
.
Plugins
(),
host
:
"here"
,
ns
:
"myns"
,
debug
:
true
,
},
{
name
:
"with envvars set"
,
args
:
[]
string
{},
envars
:
map
[
string
]
string
{
"HELM_HOME"
:
"/bar"
,
"HELM_HOST"
:
"there"
,
"HELM_DEBUG"
:
"1"
,
"TILLER_NAMESPACE"
:
"yourns"
},
home
:
"/bar"
,
plugins
:
helmpath
.
Home
(
"/bar"
)
.
Plugins
(),
host
:
"there"
,
ns
:
"yourns"
,
debug
:
true
,
},
{
name
:
"with flags and envvars set"
,
args
:
[]
string
{
"--home"
,
"/foo"
,
"--host=here"
,
"--debug"
,
"--tiller-namespace=myns"
},
envars
:
map
[
string
]
string
{
"HELM_HOME"
:
"/bar"
,
"HELM_HOST"
:
"there"
,
"HELM_DEBUG"
:
"1"
,
"TILLER_NAMESPACE"
:
"yourns"
,
"HELM_PLUGIN"
:
"glade"
},
home
:
"/foo"
,
plugins
:
"glade"
,
host
:
"here"
,
ns
:
"myns"
,
debug
:
true
,
},
}
cleanup
:=
resetEnv
()
defer
cleanup
()
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
for
k
,
v
:=
range
tt
.
envars
{
os
.
Setenv
(
k
,
v
)
}
flags
:=
pflag
.
NewFlagSet
(
"testing"
,
pflag
.
ContinueOnError
)
settings
:=
&
EnvSettings
{}
settings
.
AddFlags
(
flags
)
flags
.
Parse
(
tt
.
args
)
settings
.
Init
(
flags
)
if
settings
.
Home
!=
helmpath
.
Home
(
tt
.
home
)
{
t
.
Errorf
(
"expected home %q, got %q"
,
tt
.
home
,
settings
.
Home
)
}
if
settings
.
PluginDirs
()
!=
tt
.
plugins
{
t
.
Errorf
(
"expected plugins %q, got %q"
,
tt
.
plugins
,
settings
.
PluginDirs
())
}
if
settings
.
TillerHost
!=
tt
.
host
{
t
.
Errorf
(
"expected host %q, got %q"
,
tt
.
host
,
settings
.
TillerHost
)
}
if
settings
.
Debug
!=
tt
.
debug
{
t
.
Errorf
(
"expected debug %t, got %t"
,
tt
.
debug
,
settings
.
Debug
)
}
if
settings
.
TillerNamespace
!=
tt
.
ns
{
t
.
Errorf
(
"expected tiller-namespace %q, got %q"
,
tt
.
ns
,
settings
.
TillerNamespace
)
}
if
settings
.
KubeContext
!=
tt
.
kcontext
{
t
.
Errorf
(
"expected kube-context %q, got %q"
,
tt
.
kcontext
,
settings
.
KubeContext
)
}
cleanup
()
})
}
}
func
resetEnv
()
func
()
{
origEnv
:=
os
.
Environ
()
// ensure any local envvars do not hose us
for
_
,
e
:=
range
envMap
{
os
.
Unsetenv
(
e
)
}
return
func
()
{
for
_
,
pair
:=
range
origEnv
{
kv
:=
strings
.
SplitN
(
pair
,
"="
,
2
)
os
.
Setenv
(
kv
[
0
],
kv
[
1
])
}
}
}
pkg/plugin/plugin.go
View file @
3cf8f2c8
...
@@ -22,7 +22,6 @@ import (
...
@@ -22,7 +22,6 @@ import (
"strings"
"strings"
helm_env
"k8s.io/helm/pkg/helm/environment"
helm_env
"k8s.io/helm/pkg/helm/environment"
tiller_env
"k8s.io/helm/pkg/tiller/environment"
"github.com/ghodss/yaml"
"github.com/ghodss/yaml"
)
)
...
@@ -179,8 +178,8 @@ func SetupPluginEnv(settings helm_env.EnvSettings,
...
@@ -179,8 +178,8 @@ func SetupPluginEnv(settings helm_env.EnvSettings,
// Set vars that may not have been set, and save client the
// Set vars that may not have been set, and save client the
// trouble of re-parsing.
// trouble of re-parsing.
helm_env
.
PluginEnvVar
:
settings
.
PluginDirs
(),
"HELM_PLUGIN"
:
settings
.
PluginDirs
(),
helm_env
.
HomeEnvVar
:
settings
.
Home
.
String
(),
"HELM_HOME"
:
settings
.
Home
.
String
(),
// Set vars that convey common information.
// Set vars that convey common information.
"HELM_PATH_REPOSITORY"
:
settings
.
Home
.
Repository
(),
"HELM_PATH_REPOSITORY"
:
settings
.
Home
.
Repository
(),
...
@@ -189,8 +188,8 @@ func SetupPluginEnv(settings helm_env.EnvSettings,
...
@@ -189,8 +188,8 @@ func SetupPluginEnv(settings helm_env.EnvSettings,
"HELM_PATH_LOCAL_REPOSITORY"
:
settings
.
Home
.
LocalRepository
(),
"HELM_PATH_LOCAL_REPOSITORY"
:
settings
.
Home
.
LocalRepository
(),
"HELM_PATH_STARTER"
:
settings
.
Home
.
Starters
(),
"HELM_PATH_STARTER"
:
settings
.
Home
.
Starters
(),
"TILLER_HOST"
:
settings
.
TillerHost
,
"TILLER_HOST"
:
settings
.
TillerHost
,
tiller_env
.
TillerNamespaceEnvVar
:
settings
.
TillerNamespace
,
"TILLER_NAMESPACE"
:
settings
.
TillerNamespace
,
}
{
}
{
os
.
Setenv
(
key
,
val
)
os
.
Setenv
(
key
,
val
)
}
}
...
...
pkg/tiller/environment/environment.go
View file @
3cf8f2c8
...
@@ -37,10 +37,6 @@ import (
...
@@ -37,10 +37,6 @@ import (
"k8s.io/helm/pkg/storage/driver"
"k8s.io/helm/pkg/storage/driver"
)
)
// TillerNamespaceEnvVar is the environment variable name for the Tiller
// namespace in the kubernetes cluster.
const
TillerNamespaceEnvVar
=
"TILLER_NAMESPACE"
// DefaultTillerNamespace is the default namespace for Tiller.
// DefaultTillerNamespace is the default namespace for Tiller.
const
DefaultTillerNamespace
=
"kube-system"
const
DefaultTillerNamespace
=
"kube-system"
...
...
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