create.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. package cmd
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "github.com/fatih/color"
  8. "github.com/porter-dev/porter/cli/cmd/api"
  9. "github.com/porter-dev/porter/cli/cmd/deploy"
  10. "github.com/porter-dev/porter/cli/cmd/gitutils"
  11. "github.com/spf13/cobra"
  12. "sigs.k8s.io/yaml"
  13. )
  14. // createCmd represents the "porter create" base command when called
  15. // without any subcommands
  16. var createCmd = &cobra.Command{
  17. Use: "create [kind]",
  18. Args: cobra.ExactArgs(1),
  19. Short: "TODO.",
  20. Run: func(cmd *cobra.Command, args []string) {
  21. err := checkLoginAndRun(args, createFull)
  22. if err != nil {
  23. os.Exit(1)
  24. }
  25. },
  26. }
  27. var name string
  28. var values string
  29. var source string
  30. func init() {
  31. rootCmd.AddCommand(createCmd)
  32. createCmd.PersistentFlags().StringVar(
  33. &name,
  34. "name",
  35. "",
  36. "Name of the new application/job/worker.",
  37. )
  38. createCmd.MarkPersistentFlagRequired("name")
  39. createCmd.PersistentFlags().BoolVar(
  40. &local,
  41. "local",
  42. true,
  43. "Whether local context should be used for build",
  44. )
  45. createCmd.PersistentFlags().StringVarP(
  46. &localPath,
  47. "path",
  48. "p",
  49. ".",
  50. "If local build, the path to the build directory",
  51. )
  52. createCmd.PersistentFlags().StringVar(
  53. &namespace,
  54. "namespace",
  55. "default",
  56. "Namespace of the application",
  57. )
  58. createCmd.PersistentFlags().StringVarP(
  59. &values,
  60. "values",
  61. "v",
  62. "",
  63. "Filepath to a values.yaml file",
  64. )
  65. createCmd.PersistentFlags().StringVar(
  66. &dockerfile,
  67. "dockerfile",
  68. "",
  69. "the path to the dockerfile",
  70. )
  71. createCmd.PersistentFlags().StringVar(
  72. &method,
  73. "method",
  74. "",
  75. "the build method to use (\"docker\" or \"pack\")",
  76. )
  77. createCmd.PersistentFlags().StringVar(
  78. &source,
  79. "source",
  80. "local",
  81. "the type of source (\"local\" or \"github\")",
  82. )
  83. }
  84. var supportedKinds = map[string]string{"web": "", "job": "", "worker": ""}
  85. func createFull(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
  86. // check the kind
  87. if _, exists := supportedKinds[args[0]]; !exists {
  88. return fmt.Errorf("%s is not a supported type: specify web, job, or worker", args[0])
  89. }
  90. // read the values if necessary
  91. valuesObj, err := readValuesFile()
  92. if err != nil {
  93. return err
  94. }
  95. color.New(color.FgGreen).Printf("Creating %s release: %s\n", args[0], name)
  96. fullPath, err := filepath.Abs(localPath)
  97. if err != nil {
  98. return err
  99. }
  100. var buildMethod deploy.DeployBuildType
  101. if method != "" {
  102. buildMethod = deploy.DeployBuildType(method)
  103. } else if dockerfile != "" {
  104. buildMethod = deploy.DeployBuildTypeDocker
  105. }
  106. createAgent := &deploy.CreateAgent{
  107. Client: client,
  108. CreateOpts: &deploy.CreateOpts{
  109. SharedOpts: &deploy.SharedOpts{
  110. ProjectID: config.Project,
  111. ClusterID: config.Cluster,
  112. Namespace: namespace,
  113. LocalPath: fullPath,
  114. LocalDockerfile: dockerfile,
  115. Method: buildMethod,
  116. },
  117. Kind: args[0],
  118. ReleaseName: name,
  119. },
  120. }
  121. if source == "local" {
  122. subdomain, err := createAgent.CreateFromDocker(valuesObj)
  123. if err != nil {
  124. return err
  125. }
  126. color.New(color.FgGreen).Printf("Your web application is ready at: %s\n", subdomain)
  127. } else {
  128. return createFromGithub(createAgent, valuesObj)
  129. }
  130. return nil
  131. }
  132. func createFromGithub(createAgent *deploy.CreateAgent, overrideValues map[string]interface{}) error {
  133. fullPath, err := filepath.Abs(localPath)
  134. if err != nil {
  135. return err
  136. }
  137. _, err = gitutils.GitDirectory(fullPath)
  138. if err != nil {
  139. return err
  140. }
  141. remote, gitBranch, err := gitutils.GetRemoteBranch(fullPath)
  142. if err != nil {
  143. return err
  144. }
  145. ok, remoteRepo := gitutils.ParseGithubRemote(remote)
  146. if !ok {
  147. return fmt.Errorf("remote is not a Github repository")
  148. }
  149. subdomain, err := createAgent.CreateFromGithub(&deploy.GithubOpts{
  150. Branch: gitBranch,
  151. Repo: remoteRepo,
  152. }, overrideValues)
  153. color.New(color.FgGreen).Printf("Your web application is ready at: %s\n", subdomain)
  154. return err
  155. }
  156. func readValuesFile() (map[string]interface{}, error) {
  157. res := make(map[string]interface{})
  158. if values == "" {
  159. return res, nil
  160. }
  161. valuesFilePath, err := filepath.Abs(values)
  162. if err != nil {
  163. return nil, err
  164. }
  165. if info, err := os.Stat(valuesFilePath); os.IsNotExist(err) || info.IsDir() {
  166. return nil, fmt.Errorf("values file does not exist or is a directory")
  167. }
  168. reader, err := os.Open(valuesFilePath)
  169. if err != nil {
  170. return nil, err
  171. }
  172. bytes, err := ioutil.ReadAll(reader)
  173. if err != nil {
  174. return nil, err
  175. }
  176. err = yaml.Unmarshal(bytes, &res)
  177. if err != nil {
  178. return nil, err
  179. }
  180. return res, nil
  181. }