@@ -2,22 +2,35 @@ package cmd
22
33import (
44 "fmt"
5+ "io/ioutil"
56 "os"
7+ "path/filepath"
68 "strconv"
79 "strings"
810
11+ "k8s.io/helm/pkg/repo"
12+
13+ "github.com/covexo/devspace/pkg/util/stdinutil"
14+ "github.com/covexo/devspace/pkg/util/tar"
15+ "github.com/covexo/devspace/pkg/util/yamlutil"
16+
17+ helmClient "github.com/covexo/devspace/pkg/devspace/clients/helm"
18+ "github.com/covexo/devspace/pkg/devspace/clients/kubectl"
919 "github.com/covexo/devspace/pkg/devspace/config/configutil"
1020 "github.com/covexo/devspace/pkg/devspace/config/v1"
1121 "github.com/covexo/devspace/pkg/util/log"
22+ "github.com/russross/blackfriday"
23+ "github.com/skratchdot/open-golang/open"
1224 "github.com/spf13/cobra"
1325)
1426
1527// AddCmd holds the information needed for the add command
1628type AddCmd struct {
17- flags * AddCmdFlags
18- syncFlags * addSyncCmdFlags
19- portFlags * addPortCmdFlags
20- dsConfig * v1.DevSpaceConfig
29+ flags * AddCmdFlags
30+ syncFlags * addSyncCmdFlags
31+ portFlags * addPortCmdFlags
32+ packageFlags * addPackageFlags
33+ dsConfig * v1.DevSpaceConfig
2134}
2235
2336// AddCmdFlags holds the possible flags for the add command
@@ -37,11 +50,18 @@ type addPortCmdFlags struct {
3750 Selector string
3851}
3952
53+ type addPackageFlags struct {
54+ AppVersion string
55+ ChartVersion string
56+ SkipQuestion bool
57+ }
58+
4059func init () {
4160 cmd := & AddCmd {
42- flags : & AddCmdFlags {},
43- syncFlags : & addSyncCmdFlags {},
44- portFlags : & addPortCmdFlags {},
61+ flags : & AddCmdFlags {},
62+ syncFlags : & addSyncCmdFlags {},
63+ portFlags : & addPortCmdFlags {},
64+ packageFlags : & addPackageFlags {},
4565 }
4666
4767 addCmd := & cobra.Command {
@@ -93,7 +113,7 @@ func init() {
93113
94114 addPortCmd := & cobra.Command {
95115 Use : "port" ,
96- Short : "Lists port forwarding configuration" ,
116+ Short : "Add a new port forward configuration" ,
97117 Long : `
98118 #######################################################
99119 ################ devspace add port ####################
@@ -111,6 +131,169 @@ func init() {
111131 addPortCmd .Flags ().StringVar (& cmd .portFlags .Selector , "selector" , "" , "Comma separated key=value selector list (e.g. release=test)" )
112132
113133 addCmd .AddCommand (addPortCmd )
134+
135+ addPackageCmd := & cobra.Command {
136+ Use : "package" ,
137+ Short : "Add a helm chart" ,
138+ Long : `
139+ #######################################################
140+ ############### devspace add package ##################
141+ #######################################################
142+ Adds an existing helm chart to the devspace
143+ (run 'devspace add package' to display all available
144+ helm charts)
145+
146+ Examples:
147+ devspace add package
148+ devspace add package mysql
149+ devspace add package mysql --app-version=5.7.14
150+ devspace add package mysql --chart-version=0.10.3
151+ #######################################################
152+ ` ,
153+ Run : cmd .RunAddPackage ,
154+ }
155+
156+ addPackageCmd .Flags ().StringVar (& cmd .packageFlags .AppVersion , "app-version" , "" , "App version" )
157+ addPackageCmd .Flags ().StringVar (& cmd .packageFlags .ChartVersion , "chart-version" , "" , "Chart version" )
158+ addPackageCmd .Flags ().BoolVar (& cmd .packageFlags .SkipQuestion , "skip-question" , false , "Skips the question to show the readme in a browser" )
159+
160+ addCmd .AddCommand (addPackageCmd )
161+ }
162+
163+ // RunAddPackage executes the add package command logic
164+ func (cmd * AddCmd ) RunAddPackage (cobraCmd * cobra.Command , args []string ) {
165+ kubectl , err := kubectl .NewClient ()
166+ if err != nil {
167+ log .Fatalf ("Unable to create new kubectl client: %v" , err )
168+ }
169+
170+ helm , err := helmClient .NewClient (kubectl , false )
171+ if err != nil {
172+ log .Fatalf ("Error initializing helm client: %v" , err )
173+ }
174+
175+ if len (args ) != 1 {
176+ helm .PrintAllAvailableCharts ()
177+ return
178+ }
179+
180+ log .StartWait ("Search Chart" )
181+ repo , version , err := helm .SearchChart (args [0 ], cmd .packageFlags .ChartVersion , cmd .packageFlags .AppVersion )
182+ log .StopWait ()
183+
184+ if err != nil {
185+ log .Fatal (err )
186+ }
187+
188+ log .Done ("Chart found" )
189+
190+ cwd , err := os .Getwd ()
191+ if err != nil {
192+ log .Fatal (err )
193+ }
194+
195+ requirementsFile := filepath .Join (cwd , "chart" , "requirements.yaml" )
196+ _ , err = os .Stat (requirementsFile )
197+ if os .IsNotExist (err ) {
198+ entry := "dependencies:\n " +
199+ "- name: \" " + version .GetName () + "\" \n " +
200+ " version: \" " + version .GetVersion () + "\" \n " +
201+ " repository: \" " + repo .URL + "\" \n "
202+
203+ err = ioutil .WriteFile (requirementsFile , []byte (entry ), 0600 )
204+ if err != nil {
205+ log .Fatal (err )
206+ }
207+ } else {
208+ yamlContents := map [interface {}]interface {}{}
209+ err = yamlutil .ReadYamlFromFile (requirementsFile , yamlContents )
210+ if err != nil {
211+ log .Fatalf ("Error parsing %s: %v" , requirementsFile , err )
212+ }
213+
214+ dependenciesArr := []interface {}{}
215+ if dependencies , ok := yamlContents ["dependencies" ]; ok {
216+ dependenciesArr , ok = dependencies .([]interface {})
217+ if ok == false {
218+ log .Fatalf ("Error parsing %s: Key dependencies is not an array" , requirementsFile )
219+ }
220+ }
221+
222+ dependenciesArr = append (dependenciesArr , map [interface {}]interface {}{
223+ "name" : version .GetName (),
224+ "version" : version .GetVersion (),
225+ "repository" : repo .URL ,
226+ })
227+ yamlContents ["dependencies" ] = dependenciesArr
228+
229+ err = yamlutil .WriteYamlToFile (yamlContents , requirementsFile )
230+ if err != nil {
231+ log .Fatal (err )
232+ }
233+ }
234+
235+ log .StartWait ("Update chart dependencies" )
236+ err = helm .UpdateDependencies (filepath .Join (cwd , "chart" ))
237+ log .StopWait ()
238+
239+ if err != nil {
240+ log .Fatal (err )
241+ }
242+
243+ f , err := os .OpenFile (filepath .Join (cwd , "chart" , "values.yaml" ), os .O_APPEND | os .O_WRONLY | os .O_CREATE , 0600 )
244+ if err != nil {
245+ log .Fatal (err )
246+ }
247+
248+ defer f .Close ()
249+ if _ , err = f .WriteString ("\n " + version .GetName () + ": {}\n " ); err != nil {
250+ log .Fatal (err )
251+ }
252+
253+ log .Donef ("Successfully added %s as chart dependency, you can configure the package in 'chart/values.yaml'" , version .GetName ())
254+ cmd .showReadme (version )
255+ }
256+
257+ func (cmd * AddCmd ) showReadme (chartVersion * repo.ChartVersion ) {
258+ cwd , err := os .Getwd ()
259+ if err != nil {
260+ log .Fatal (err )
261+ }
262+
263+ if cmd .packageFlags .SkipQuestion {
264+ return
265+ }
266+
267+ showReadme := * stdinutil .GetFromStdin (& stdinutil.GetFromStdinParams {
268+ Question : "Do you want to open the package README? (y|n)" ,
269+ DefaultValue : "y" ,
270+ ValidationRegexPattern : "^(y|n)" ,
271+ })
272+
273+ if showReadme == "n" {
274+ return
275+ }
276+
277+ content , err := tar .ExtractSingleFileToStringTarGz (filepath .Join (cwd , "chart" , "charts" , chartVersion .GetName ()+ "-" + chartVersion .GetVersion ()+ ".tgz" ), chartVersion .GetName ()+ "/README.md" )
278+ if err != nil {
279+ log .Fatal (err )
280+ }
281+
282+ output := blackfriday .MarkdownCommon ([]byte (content ))
283+ f , err := os .OpenFile (filepath .Join (os .TempDir (), "Readme.html" ), os .O_RDWR | os .O_CREATE , 0600 )
284+ if err != nil {
285+ log .Fatal (err )
286+ }
287+
288+ defer f .Close ()
289+
290+ _ , err = f .Write (output )
291+ if err != nil {
292+ log .Fatal (err )
293+ }
294+
295+ f .Close ()
296+ open .Start (f .Name ())
114297}
115298
116299// RunAddSync executes the add sync command logic
@@ -161,7 +344,6 @@ func (cmd *AddCmd) RunAddSync(cobraCmd *cobra.Command, args []string) {
161344 config .DevSpace .Sync = & syncConfig
162345
163346 err = configutil .SaveConfig ()
164-
165347 if err != nil {
166348 log .Fatalf ("Couldn't save config file: %s" , err .Error ())
167349 }
0 commit comments