Move commands to where they should be (#1002)

* Move main to cmd/odo.

* Refactor doc generation into one single command.

* Rename for consistency.

* Move commands to pkg/odo/cli.

* Export Add*Flag functions to prepare for move.

* Move validateName to pkg/odo/cli/util/validation.go.

* Move functions used by only one command to the command file.

* Renamed to storage_test since tests are storage-related.

* Move usage template to cmdutils.

* Use RootCmd() instead of var access.

* Move printComponentInfo back to cmdutils and expose.

* Move application to application package.

* Move component to component package. Move componentShortFlag also.
Not sure why it was on delete and not on component.

* Move commands to appropriate packages.

* Rename root to cli.

* Rename main to odo.

* Fix wrong reference to main file.

* Fix(?) generate-coverage.sh.

* Move root command name to cli.go.

* Move Add*Flag functions to genericclioptions/context_flags.
Move adding completion handler to package-specific functions
(duplication >_<)

* Move CmdUsageTemplate and PrintComponentInfo to odo/util package.

* Move VERSION and getLatestReleaseInfo to cli/version package.
Remove now empty cmdutils.

* Start using NewCmd* instead of init. Break import cycle. :)

* Rename client to cmdutils.

* Move to use NewCmd* pattern for all commands.

* Fix cross-compilation target.
This commit is contained in:
Chris Laprun
2018-11-22 08:52:03 +01:00
committed by Suraj Narwade
parent b21872d31d
commit 6d05c4ba9d
40 changed files with 589 additions and 529 deletions

View File

@@ -1,320 +0,0 @@
package cmd
import (
"fmt"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util"
"os"
"strings"
"github.com/redhat-developer/odo/pkg/odo/util/completion"
"text/tabwriter"
"github.com/redhat-developer/odo/pkg/application"
"github.com/redhat-developer/odo/pkg/component"
"github.com/spf13/cobra"
)
var (
applicationShortFlag bool
applicationForceDeleteFlag bool
)
// applicationCmd represents the app command
var applicationCmd = &cobra.Command{
Use: "app",
Short: "Perform application operations",
Long: `Performs application operations related to your OpenShift project.`,
Example: fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s",
applicationCreateCmd.Example,
applicationGetCmd.Example,
applicationDeleteCmd.Example,
applicationDescribeCmd.Example,
applicationListCmd.Example,
applicationSetCmd.Example),
Aliases: []string{"application"},
// 'odo app' is the same as 'odo app get'
// 'odo app <application_name>' is the same as 'odo app set <application_name>'
Run: func(cmd *cobra.Command, args []string) {
if len(args) > 0 && args[0] != "get" && args[0] != "set" {
applicationSetCmd.Run(cmd, args)
} else {
applicationGetCmd.Run(cmd, args)
}
},
}
var applicationCreateCmd = &cobra.Command{
Use: "create",
Short: "Create an application",
Long: `Create an application.
If no app name is passed, a default app name will be auto-generated.
`,
Example: ` # Create an application
odo app create myapp
odo app create
`,
Args: cobra.RangeArgs(0, 1),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
projectName := context.Project
var appName string
if len(args) == 1 {
// The only arg passed is the app name
appName = args[0]
} else {
// Desired app name is not passed so, generate a new app name
// Fetch existing list of apps
apps, err := application.List(client)
util.CheckError(err, "")
// Generate a random name that's not already in use for the existing apps
appName, err = application.GetDefaultAppName(apps)
util.CheckError(err, "")
}
// validate application name
err := validateName(appName)
util.CheckError(err, "")
fmt.Printf("Creating application: %v in project: %v\n", appName, projectName)
err = application.Create(client, appName)
util.CheckError(err, "")
err = application.SetCurrent(client, appName)
// TODO: updating the app name should be done via SetCurrent and passing the Context
// not strictly needed here but Context should stay in sync
context.Application = appName
util.CheckError(err, "")
fmt.Printf("Switched to application: %v in project: %v\n", appName, projectName)
},
}
var applicationGetCmd = &cobra.Command{
Use: "get",
Short: "Get the active application",
Long: "Get the active application",
Example: ` # Get the currently active application
odo app get
`,
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
projectName := context.Project
app := context.Application
if applicationShortFlag {
fmt.Print(app)
return
}
if app == "" {
fmt.Printf("There's no active application.\nYou can create one by running 'odo application create <name>'.\n")
return
}
fmt.Printf("The current application is: %v in project: %v\n", app, projectName)
},
}
var applicationDeleteCmd = &cobra.Command{
Use: "delete",
Short: "Delete the given application",
Long: "Delete the given application",
Example: ` # Delete the application
odo app delete myapp
`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
projectName := context.Project
appName := context.Application
if len(args) == 1 {
// If app name passed, consider it for deletion
appName = args[0]
}
var confirmDeletion string
// Print App Information which will be deleted
err := printDeleteAppInfo(client, appName)
util.CheckError(err, "")
exists, err := application.Exists(client, appName)
util.CheckError(err, "")
if !exists {
fmt.Printf("Application %v in project %v does not exist\n", appName, projectName)
os.Exit(1)
}
if applicationForceDeleteFlag {
confirmDeletion = "y"
} else {
fmt.Printf("Are you sure you want to delete the application: %v from project: %v? [y/N] ", appName, projectName)
fmt.Scanln(&confirmDeletion)
}
if strings.ToLower(confirmDeletion) == "y" {
err := application.Delete(client, appName)
util.CheckError(err, "")
fmt.Printf("Deleted application: %s from project: %v\n", appName, projectName)
} else {
fmt.Printf("Aborting deletion of application: %v\n", appName)
}
},
}
var applicationListCmd = &cobra.Command{
Use: "list",
Short: "List all applications in the current project",
Long: "List all applications in the current project.",
Example: ` # List all applications in the current project
odo app list
# List all applications in the specified project
odo app list --project myproject
`,
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
projectName := context.Project
apps, err := application.ListInProject(client)
util.CheckError(err, "unable to get list of applications")
if len(apps) > 0 {
fmt.Printf("The project '%v' has the following applications:\n", projectName)
tabWriter := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
fmt.Fprintln(tabWriter, "ACTIVE", "\t", "NAME")
for _, app := range apps {
activeMark := " "
if app.Active {
activeMark = "*"
}
fmt.Fprintln(tabWriter, activeMark, "\t", app.Name)
}
tabWriter.Flush()
} else {
fmt.Printf("There are no applications deployed in the project '%v'.\n", projectName)
}
},
}
var applicationSetCmd = &cobra.Command{
Use: "set",
Short: "Set application as active",
Long: "Set application as active",
Example: ` # Set an application as active
odo app set myapp
`,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("Please provide application name")
}
if len(args) > 1 {
return fmt.Errorf("Only one argument (application name) is allowed")
}
return nil
}, Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
projectName := context.Project
// error if application does not exist
appName := args[0]
exists, err := application.Exists(client, appName)
util.CheckError(err, "unable to check if application exists")
if !exists {
fmt.Printf("Application %v does not exist\n", appName)
os.Exit(1)
}
err = application.SetCurrent(client, appName)
util.CheckError(err, "")
fmt.Printf("Switched to application: %v in project: %v\n", args[0], projectName)
// TODO: updating the app name should be done via SetCurrent and passing the Context
// not strictly needed here but Context should stay in sync
context.Application = appName
},
}
var applicationDescribeCmd = &cobra.Command{
Use: "describe [application_name]",
Short: "Describe the given application",
Long: "Describe the given application",
Args: cobra.MaximumNArgs(1),
Example: ` # Describe webapp application,
odo app describe webapp
`,
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
projectName := context.Project
appName := context.Application
if len(args) == 0 {
if appName == "" {
fmt.Printf("There's no active application in project: %v\n", projectName)
os.Exit(1)
}
} else {
appName = args[0]
//Check whether application exist or not
exists, err := application.Exists(client, appName)
util.CheckError(err, "")
if !exists {
fmt.Printf("Application with the name %s does not exist in %s \n", appName, projectName)
os.Exit(1)
}
}
// List of Component
componentList, err := component.List(client, appName)
util.CheckError(err, "")
if len(componentList) == 0 {
fmt.Printf("Application %s has no components deployed.\n", appName)
os.Exit(1)
}
fmt.Printf("Application %s has:\n", appName)
for _, currentComponent := range componentList {
componentType, path, componentURL, appStore, err := component.GetComponentDesc(client, currentComponent.Name, appName)
util.CheckError(err, "")
printComponentInfo(currentComponent.Name, componentType, path, componentURL, appStore)
}
},
}
func init() {
applicationDeleteCmd.Flags().BoolVarP(&applicationForceDeleteFlag, "force", "f", false, "Delete application without prompting")
applicationGetCmd.Flags().BoolVarP(&applicationShortFlag, "short", "q", false, "If true, display only the application name")
// add flags from 'get' to application command
applicationCmd.Flags().AddFlagSet(applicationGetCmd.Flags())
applicationCmd.AddCommand(applicationListCmd)
applicationCmd.AddCommand(applicationDeleteCmd)
applicationCmd.AddCommand(applicationGetCmd)
applicationCmd.AddCommand(applicationCreateCmd)
applicationCmd.AddCommand(applicationSetCmd)
applicationCmd.AddCommand(applicationDescribeCmd)
//Adding `--project` flag
addProjectFlag(applicationListCmd)
addProjectFlag(applicationCreateCmd)
addProjectFlag(applicationDeleteCmd)
addProjectFlag(applicationDescribeCmd)
addProjectFlag(applicationSetCmd)
addProjectFlag(applicationGetCmd)
// Add a defined annotation in order to appear in the help menu
applicationCmd.Annotations = map[string]string{"command": "other"}
applicationCmd.SetUsageTemplate(cmdUsageTemplate)
rootCmd.AddCommand(applicationCmd)
completion.RegisterCommandHandler(applicationDescribeCmd, completion.AppCompletionHandler)
completion.RegisterCommandHandler(applicationDeleteCmd, completion.AppCompletionHandler)
completion.RegisterCommandHandler(applicationSetCmd, completion.AppCompletionHandler)
}

View File

@@ -1,303 +0,0 @@
package cmd
import (
"fmt"
"github.com/olekukonko/tablewriter"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util"
"os"
"strings"
"text/tabwriter"
"github.com/redhat-developer/odo/pkg/catalog"
svc "github.com/redhat-developer/odo/pkg/service"
"github.com/spf13/cobra"
)
var catalogCmd = &cobra.Command{
Use: "catalog [options]",
Short: "Catalog related operations",
Long: "Catalog related operations",
Example: fmt.Sprintf("%s\n%s\n%s",
catalogListCmd.Example,
catalogSearchCmd.Example,
catalogDescribeCmd.Example),
}
var catalogListCmd = &cobra.Command{
Use: "list",
Short: "List all available component & service types.",
Long: "List all available component and service types from OpenShift",
Example: ` # Get the supported components
odo catalog list components
# Get the supported services from service catalog
odo catalog list services
`,
}
var catalogListComponentCmd = &cobra.Command{
Use: "components",
Short: "List all components available.",
Long: "List all available component types from OpenShift's Image Builder.",
Example: ` # Get the supported components
odo catalog list components
# Search for a supported component
odo catalog search component nodejs
`,
Run: func(cmd *cobra.Command, args []string) {
client := genericclioptions.Client(cmd)
catalogList, err := catalog.List(client)
util.CheckError(err, "unable to list components")
switch len(catalogList) {
case 0:
fmt.Printf("No deployable components found\n")
default:
currentProject := client.GetCurrentProjectName()
w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
fmt.Fprintln(w, "NAME", "\t", "PROJECT", "\t", "TAGS")
for _, component := range catalogList {
componentName := component.Name
if component.Namespace == currentProject {
/*
If current namespace is same as the current component namespace,
Loop through every other component,
If there exists a component with same name but in different namespaces,
mark the one from current namespace with (*)
*/
for _, comp := range catalogList {
if comp.Name == component.Name && component.Namespace != comp.Namespace {
componentName = fmt.Sprintf("%s (*)", component.Name)
}
}
}
fmt.Fprintln(w, componentName, "\t", component.Namespace, "\t", strings.Join(component.Tags, ","))
}
w.Flush()
}
},
}
var catalogListServiceCmd = &cobra.Command{
Use: "services",
Short: "Lists all available services",
Long: "Lists all available services",
Example: ` # List all services
odo catalog list services
# Search for a supported service
odo catalog search service mysql
`,
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
client := genericclioptions.Client(cmd)
catalogList, err := svc.ListCatalog(client)
util.CheckError(err, "unable to list services because Service Catalog is not enabled in your cluster")
switch len(catalogList) {
case 0:
fmt.Printf("No deployable services found\n")
default:
w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
fmt.Fprintln(w, "NAME", "\t", "PLANS")
for _, service := range catalogList {
fmt.Fprintln(w, service.Name, "\t", strings.Join(service.PlanList, ","))
}
w.Flush()
}
},
}
var catalogSearchCmd = &cobra.Command{
Use: "search",
Short: "Search available component & service types.",
Long: `Search available component & service types..
This searches for a partial match for the given search term in all the available
components & services.
`,
Example: ` # Search for a component
odo catalog search component python
# Search for a service
odo catalog search service mysql
`,
}
var catalogSearchComponentCmd = &cobra.Command{
Use: "component",
Short: "Search component type in catalog",
Long: `Search component type in catalog.
This searches for a partial match for the given search term in all the available
components.
`,
Args: cobra.ExactArgs(1),
Example: ` # Search for a component
odo catalog search component python
`,
Run: func(cmd *cobra.Command, args []string) {
client := genericclioptions.Client(cmd)
searchTerm := args[0]
components, err := catalog.Search(client, searchTerm)
util.CheckError(err, "unable to search for components")
switch len(components) {
case 0:
fmt.Printf("No component matched the query: %v\n", searchTerm)
default:
fmt.Println("The following components were found:")
for _, component := range components {
fmt.Printf("- %v\n", component)
}
}
},
}
var catalogSearchServiceCmd = &cobra.Command{
Use: "service",
Short: "Search service type in catalog",
Long: `Search service type in catalog.
This searches for a partial match for the given search term in all the available
services from service catalog.
`,
Example: ` # Search for a service
odo catalog search service mysql
`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
client := genericclioptions.Client(cmd)
searchTerm := args[0]
components, err := svc.Search(client, searchTerm)
util.CheckError(err, "unable to search for services")
switch len(components) {
case 0:
fmt.Printf("No service matched the query: %v\n", searchTerm)
default:
w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
fmt.Fprintln(w, "NAME", "\t", "PLANS")
for _, component := range components {
fmt.Fprintln(w, component.Name, "\t", strings.Join(component.PlanList, ","))
}
w.Flush()
}
},
}
var catalogDescribeCmd = &cobra.Command{
Use: "describe",
Short: "Describe catalog item",
Long: "Describe the given catalog item from OpenShift",
Args: cobra.ExactArgs(1),
Example: ` # Describe the given service
odo catalog describe service mysql-persistent
`,
}
var catalogDescribeServiceCmd = &cobra.Command{
Use: "service",
Short: "Describe a service",
Long: `Describe a service type.
This describes the service and the associated plans.
`,
Example: ` # Describe a service
odo catalog describe service mysql-persistent
`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
client := genericclioptions.Client(cmd)
serviceName := args[0]
service, plans, err := svc.GetServiceClassAndPlans(client, serviceName)
util.CheckError(err, "")
table := tablewriter.NewWriter(os.Stdout)
table.SetBorder(false)
table.SetAlignment(tablewriter.ALIGN_LEFT)
serviceData := [][]string{
{"Name", service.Name},
{"Bindable", fmt.Sprint(service.Bindable)},
{"Operated by the broker", service.ServiceBrokerName},
{"Short Description", service.ShortDescription},
{"Long Description", service.LongDescription},
{"Versions Available", strings.Join(service.VersionsAvailable, ",")},
{"Tags", strings.Join(service.Tags, ",")},
}
table.AppendBulk(serviceData)
table.Append([]string{""})
if len(plans) > 0 {
table.Append([]string{"PLANS"})
for _, plan := range plans {
// create the display values for required and optional parameters
requiredWithMandatoryUserInputParameterNames := []string{}
requiredWithOptionalUserInputParameterNames := []string{}
optionalParameterDisplay := []string{}
for _, parameter := range plan.Parameters {
if parameter.Required {
// until we have a better solution for displaying the plan data (like a separate table perhaps)
// this is simplest thing to do
if parameter.HasDefaultValue {
defaultValueStr := fmt.Sprintf("%v", parameter.DefaultValue)
requiredWithOptionalUserInputParameterNames = append(
requiredWithOptionalUserInputParameterNames,
fmt.Sprintf("%s (default: '%s')", parameter.Name, defaultValueStr))
} else {
requiredWithMandatoryUserInputParameterNames = append(requiredWithMandatoryUserInputParameterNames, parameter.Name)
}
} else {
optionalParameterDisplay = append(optionalParameterDisplay, parameter.Name)
}
}
table.Append([]string{"***********************", "*****************************************************"})
planLineSeparator := []string{"-----------------", "-----------------"}
planData := [][]string{
{"Name", plan.Name},
planLineSeparator,
{"Display Name", plan.DisplayName},
planLineSeparator,
{"Short Description", plan.Description},
planLineSeparator,
{"Required Params without a default value", strings.Join(requiredWithMandatoryUserInputParameterNames, ", ")},
planLineSeparator,
{"Required Params with a default value", strings.Join(requiredWithOptionalUserInputParameterNames, ", ")},
planLineSeparator,
{"Optional Params", strings.Join(optionalParameterDisplay, ", ")},
{"", ""},
}
table.AppendBulk(planData)
}
table.Render()
} else {
fmt.Printf("No plans found for service %s\n", serviceName)
}
},
}
func init() {
catalogCmd.AddCommand(catalogSearchCmd)
catalogCmd.AddCommand(catalogListCmd)
catalogCmd.AddCommand(catalogDescribeCmd)
catalogListCmd.AddCommand(catalogListComponentCmd)
catalogListCmd.AddCommand(catalogListServiceCmd)
catalogSearchCmd.AddCommand(catalogSearchComponentCmd)
catalogSearchCmd.AddCommand(catalogSearchServiceCmd)
catalogDescribeCmd.AddCommand(catalogDescribeServiceCmd)
// Add a defined annotation in order to appear in the help menu
catalogCmd.Annotations = map[string]string{"command": "other"}
catalogCmd.SetUsageTemplate(cmdUsageTemplate)
rootCmd.AddCommand(catalogCmd)
}

View File

@@ -1,11 +1,14 @@
package cmd
package main
import (
"bytes"
"fmt"
"github.com/olekukonko/tablewriter"
"github.com/pkg/errors"
"github.com/redhat-developer/odo/pkg/odo/cli"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"os"
)
/*
@@ -86,11 +89,74 @@ func referencePrinter(command *cobra.Command, level int) string {
"```sh\n"+command.Example+"\n```",
command.Long,
tableOutput.String(),
"```sh\n"+GenerateCLIStructure()+"\n```",
"```sh\n"+fmt.Sprint(commandPrinter(command, 0))+"\n```",
commandOutput)
}
// Generates and returns a markdown-formatted CLI reference page for Odo
func GenerateCLIReference() string {
return referencePrinter(rootCmd, 0)
func getFlags(flags *pflag.FlagSet) []string {
var f []string
flags.VisitAll(func(flag *pflag.Flag) {
f = append(f, fmt.Sprintf("--%v", flag.Name))
})
return f
}
func flattenFlags(flags []string) string {
var flagString string
for _, flag := range flags {
flagString = flagString + flag + " "
}
return flagString
}
func commandPrinter(command *cobra.Command, level int) string {
var finalCommand string
// add indentation
for i := 0; i < level; i++ {
finalCommand = finalCommand + " "
}
finalCommand = finalCommand +
command.Name() +
" " +
flattenFlags(getFlags(command.NonInheritedFlags())) +
": " +
command.Short +
"\n"
for _, subcommand := range command.Commands() {
finalCommand = finalCommand + commandPrinter(subcommand, level+1)
}
return finalCommand
}
// Generates and returns a markdown-formatted CLI reference page for Odo
func main() {
var clidoc = &cobra.Command{
Use: "cli-doc",
Short: "Generate CLI reference for Odo",
Example: ` # Generate a markdown-formatted CLI reference page for Odo
cli-doc reference > docs/cli-reference.md
# Generate the CLI structure
cli-doc structure`,
Args: cobra.OnlyValidArgs,
ValidArgs: []string{"help", "reference", "structure"},
Run: func(command *cobra.Command, args []string) {
switch args[0] {
case "reference":
fmt.Print(referencePrinter(cli.RootCmd(), 0))
case "structure":
fmt.Print(commandPrinter(cli.RootCmd(), 0))
default:
fmt.Print(command.Usage())
}
},
}
err := clidoc.Execute()
if err != nil {
fmt.Println(errors.Cause(err))
os.Exit(1)
}
}

View File

@@ -1,47 +0,0 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func getFlags(flags *pflag.FlagSet) []string {
var f []string
flags.VisitAll(func(flag *pflag.Flag) {
f = append(f, fmt.Sprintf("--%v", flag.Name))
})
return f
}
func flattenFlags(flags []string) string {
var flagString string
for _, flag := range flags {
flagString = flagString + flag + " "
}
return flagString
}
func commandPrinter(command *cobra.Command, level int) string {
var finalCommand string
// add indentation
for i := 0; i < level; i++ {
finalCommand = finalCommand + " "
}
finalCommand = finalCommand +
command.Name() +
" " +
flattenFlags(getFlags(command.NonInheritedFlags())) +
": " +
command.Short +
"\n"
for _, subcommand := range command.Commands() {
finalCommand = finalCommand + commandPrinter(subcommand, level+1)
}
return finalCommand
}
func GenerateCLIStructure() string {
return commandPrinter(rootCmd, 0)
}

View File

@@ -1,164 +0,0 @@
package cmd
import (
"fmt"
"github.com/redhat-developer/odo/pkg/odo/util"
"github.com/redhat-developer/odo/pkg/odo/util/completion"
"github.com/spf13/cobra"
"os"
"strings"
"text/tabwriter"
"github.com/pkg/errors"
"github.com/redhat-developer/odo/pkg/component"
"github.com/redhat-developer/odo/pkg/occlient"
"github.com/redhat-developer/odo/pkg/storage"
"k8s.io/apimachinery/pkg/util/validation"
)
// printDeleteAppInfo will print things which will be deleted
func printDeleteAppInfo(client *occlient.Client, appName string) error {
componentList, err := component.List(client, appName)
if err != nil {
return errors.Wrap(err, "failed to get Component list")
}
for _, currentComponent := range componentList {
_, _, componentURL, appStore, err := component.GetComponentDesc(client, currentComponent.Name, appName)
if err != nil {
return errors.Wrap(err, "unable to get component description")
}
fmt.Println("Component", currentComponent.Name, "will be deleted.")
if len(componentURL) != 0 {
fmt.Println(" Externally exposed URL will be removed")
}
for _, store := range appStore {
fmt.Println(" Storage", store.Name, "of size", store.Size, "will be removed")
}
}
return nil
}
// printComponentInfo prints Component Information like path, URL & storage
func printComponentInfo(currentComponentName string, componentType string, path string, componentURL string, appStore []storage.StorageInfo) {
// Source
if path != "" {
fmt.Println("Component", currentComponentName, "of type", componentType, "with source in", path)
}
// URL
if componentURL != "" {
fmt.Println("Externally exposed via", componentURL)
}
// Storage
for _, store := range appStore {
fmt.Println("Storage", store.Name, "of size", store.Size)
}
}
// validateName will do validation of application & component names
// Criteria for valid name in kubernetes: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/
func validateName(name string) error {
errorList := validation.IsDNS1123Label(name)
if len(errorList) != 0 {
return errors.New(fmt.Sprintf("%s is not a valid name: %s", name, strings.Join(errorList, " ")))
}
return nil
}
// validateStoragePath will validate storagePath, if there is any existing storage with similar path, it will give an error
func validateStoragePath(client *occlient.Client, storagePath, componentName, applicationName string) error {
storeList, err := storage.List(client, componentName, applicationName)
if err != nil {
return err
}
for _, store := range storeList {
if store.Path == storagePath {
return errors.Errorf("there already is a storage mounted at %s", storagePath)
}
}
return nil
}
// printMountedStorageInComponent prints all the mounted storage in a given component of the application
func printMountedStorageInComponent(client *occlient.Client, componentName string, applicationName string) {
// defining the column structure of the table
tabWriterMounted := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
// create headers of mounted storage table
fmt.Fprintln(tabWriterMounted, "NAME", "\t", "SIZE", "\t", "PATH")
storageListMounted, err := storage.ListMounted(client, componentName, applicationName)
util.CheckError(err, "could not get mounted storage list")
// iterating over all mounted storage and put in the mount storage table
if len(storageListMounted) > 0 {
for _, mStorage := range storageListMounted {
fmt.Fprintln(tabWriterMounted, mStorage.Name, "\t", mStorage.Size, "\t", mStorage.Path)
}
// print all mounted storage of the given component
fmt.Printf("The component '%v' has the following storage attached:\n", componentName)
tabWriterMounted.Flush()
} else {
fmt.Printf("The component '%v' has no storage attached\n", componentName)
}
fmt.Println("")
}
// printMountedStorageInAllComponent prints all the mounted storage in all the components of the application and project
func printMountedStorageInAllComponent(client *occlient.Client, applicationName string) {
componentList, err := component.List(client, applicationName)
util.CheckError(err, "could not get component list")
// iterating over all the components in the given aplication and project
for _, component := range componentList {
printMountedStorageInComponent(client, component.Name, applicationName)
}
}
// printUnmountedStorage prints all the unmounted storage in the application
func printUnmountedStorage(client *occlient.Client, applicationName string) {
// defining the column structure of the unmounted storage table
tabWriterUnmounted := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
// create header of unmounted storage in all the components of the given application and project
fmt.Fprintln(tabWriterUnmounted, "NAME", "\t", "SIZE")
storageListUnmounted, err := storage.ListUnmounted(client, applicationName)
util.CheckError(err, "could not get unmounted storage list")
// iterating over all unmounted storage and put in the unmount storage table
if len(storageListUnmounted) > 0 {
for _, uStorage := range storageListUnmounted {
fmt.Fprintln(tabWriterUnmounted, uStorage.Name, "\t", uStorage.Size)
}
// print unmounted storage of all the application
fmt.Printf("Storage that are not mounted to any component:\n")
tabWriterUnmounted.Flush()
}
fmt.Println("")
}
func addProjectFlag(cmd *cobra.Command) {
cmd.Flags().String(util.ProjectFlagName, "", "Project, defaults to active project")
completion.RegisterCommandFlagHandler(cmd, "project", completion.ProjectNameCompletionHandler)
}
func addComponentFlag(cmd *cobra.Command) {
cmd.Flags().String(util.ComponentFlagName, "", "Component, defaults to active component.")
}
func addApplicationFlag(cmd *cobra.Command) {
cmd.Flags().String(util.ApplicationFlagName, "", "Application, defaults to active application")
}

View File

@@ -1,214 +0,0 @@
package cmd
import (
"fmt"
"reflect"
"testing"
appsv1 "github.com/openshift/api/apps/v1"
"github.com/redhat-developer/odo/pkg/occlient"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ktesting "k8s.io/client-go/testing"
)
func Test_validateName(t *testing.T) {
tests := []struct {
testCase string
name string
wantErr bool
}{
{
testCase: "Test case - 1",
name: "app",
wantErr: false,
},
{
testCase: "Test case - 2",
name: "app123",
wantErr: false,
},
{
testCase: "Test case - 3",
name: "app-123",
wantErr: false,
},
{
testCase: "Test case - 4",
name: "app.123",
wantErr: true,
},
{
testCase: "Test case - 5",
name: "app_123",
wantErr: true,
},
{
testCase: "Test case - 6",
name: "App",
wantErr: true,
},
{
testCase: "Test case - 7",
name: "b7pdkva7ynxf8qoyuh02tbgu2ufcy4jq7udyom7it0g8gouc39x3gy0p1wrsbt6",
wantErr: false,
},
{
testCase: "Test case - 8",
name: "b7pdkva7ynxf8qoyuh02tbgu2ufcy4jq7udyom7it0g8gouc39x3gy0p1wrsbt6p",
wantErr: true,
},
}
for _, tt := range tests {
t.Log("Running test", tt.testCase)
t.Run(tt.testCase, func(t *testing.T) {
if err := validateName(tt.name); (err != nil) != tt.wantErr {
t.Errorf("Expected error = %v, But got = %v", tt.wantErr, err)
}
})
}
}
func Test_validateStoragePath(t *testing.T) {
type args struct {
storagePath, componentName, applicationName string
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "Test Case 1",
args: args{
storagePath: "/opt/app-root/src/storage/",
componentName: "nodejs",
applicationName: "app",
},
wantErr: true,
},
{
name: "Test Case 2",
args: args{
storagePath: "/opt/app-root/src/storage/test",
componentName: "nodejs",
applicationName: "app",
},
wantErr: false,
},
}
pvcList := v1.PersistentVolumeClaimList{
Items: []v1.PersistentVolumeClaim{
{
ObjectMeta: metav1.ObjectMeta{
Name: "mystorage-app-pvc",
Labels: map[string]string{
"app.kubernetes.io/component-name": "nodejs",
"app.kubernetes.io/name": "app",
"app.kubernetes.io/storage-name": "mystorage",
},
Namespace: "myproject",
},
},
},
}
pvc := v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Name: "mystorage-app-pvc",
Labels: map[string]string{
"app.kubernetes.io/component-name": "nodejs",
"app.kubernetes.io/name": "app",
"app.kubernetes.io/storage-name": "mystorage",
},
Namespace: "myproject",
},
}
listOfDC := appsv1.DeploymentConfigList{
Items: []appsv1.DeploymentConfig{
{
ObjectMeta: metav1.ObjectMeta{
Name: "nodejs-app",
Namespace: "myproject",
Labels: map[string]string{
"app.kubernetes.io/component-name": "nodejs",
"app.kubernetes.io/component-type": "nodejs",
"app.kubernetes.io/name": "app",
},
},
Spec: appsv1.DeploymentConfigSpec{
Template: &v1.PodTemplateSpec{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
VolumeMounts: []v1.VolumeMount{
{
MountPath: "/opt/app-root/src/storage/",
Name: "mystorage-app-pvc-idrcg-volume",
},
},
},
},
Volumes: []v1.Volume{
{
Name: "mystorage-app-pvc-idrcg-volume",
VolumeSource: v1.VolumeSource{
PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
ClaimName: "mystorage-app-pvc",
},
},
},
},
},
},
},
},
},
}
labelSelector := "app.kubernetes.io/component-name=nodejs,app.kubernetes.io/name=app"
storageSelector := "app.kubernetes.io/storage-name"
client, fakeClientSet := occlient.FakeNew()
fakeClientSet.AppsClientset.PrependReactor("list", "deploymentconfigs", func(action ktesting.Action) (bool, runtime.Object, error) {
if !reflect.DeepEqual(action.(ktesting.ListAction).GetListRestrictions().Labels.String(), labelSelector) {
return true, nil, fmt.Errorf("labels not matching with expected values, expected:%s, got:%s", labelSelector, action.(ktesting.ListAction).GetListRestrictions())
}
return true, &listOfDC, nil
})
fakeClientSet.Kubernetes.PrependReactor("get", "persistentvolumeclaims", func(action ktesting.Action) (bool, runtime.Object, error) {
pvcName := action.(ktesting.GetAction).GetName()
if pvcName != pvcList.Items[0].Name {
return true, nil, fmt.Errorf("'get' called with different pvcName")
}
return true, &pvc, nil
})
fakeClientSet.Kubernetes.PrependReactor("list", "persistentvolumeclaims", func(action ktesting.Action) (bool, runtime.Object, error) {
if !reflect.DeepEqual(action.(ktesting.ListAction).GetListRestrictions().Labels.String(), storageSelector) {
return true, nil, fmt.Errorf("labels not matching with expected values, expected:%s, got:%s", storageSelector, action.(ktesting.ListAction).GetListRestrictions())
}
return true, &pvcList, nil
})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateStoragePath(client, tt.args.storagePath, tt.args.componentName, tt.args.applicationName)
if err != nil && tt.wantErr == false {
t.Errorf("test failed, expected error: nil, but got: %#v", err)
}
})
}
}

View File

@@ -1,98 +0,0 @@
package cmd
import (
"fmt"
"github.com/golang/glog"
"github.com/redhat-developer/odo/pkg/component"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util"
"github.com/spf13/cobra"
)
// componentCmd represents the component command
var componentCmd = &cobra.Command{
Use: "component",
Short: "Components of application.",
Example: fmt.Sprintf("%s\n%s",
componentGetCmd.Example,
componentSetCmd.Example),
// 'odo component' is the same as 'odo component get'
// 'odo component <component_name>' is the same as 'odo component set <component_name>'
Run: func(cmd *cobra.Command, args []string) {
if len(args) > 0 && args[0] != "get" && args[0] != "set" {
componentSetCmd.Run(cmd, args)
} else {
componentGetCmd.Run(cmd, args)
}
},
}
var componentGetCmd = &cobra.Command{
Use: "get",
Short: "Get currently active component",
Long: "Get currently active component.",
Example: ` # Get the currently active component
odo component get
`,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
glog.V(4).Infof("component get called")
context := genericclioptions.NewContext(cmd)
component := context.ComponentAllowingEmpty(true)
if componentShortFlag {
fmt.Print(component)
} else {
if component == "" {
fmt.Printf("No component is set as current\n")
return
}
fmt.Printf("The current component is: %v\n", component)
}
},
}
var componentSetCmd = &cobra.Command{
Use: "set",
Short: "Set active component.",
Long: "Set component as active.",
Example: ` # Set component named 'frontend' as active
odo set component frontend
`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
projectName := context.Project
applicationName := context.Application
componentName := context.Component(args[0])
err := component.SetCurrent(componentName, applicationName, projectName)
util.CheckError(err, "")
fmt.Printf("Switched to component: %v\n", componentName)
},
}
func init() {
componentGetCmd.Flags().BoolVarP(&componentShortFlag, "short", "q", false, "If true, display only the component name")
// add flags from 'get' to component command
componentCmd.Flags().AddFlagSet(componentGetCmd.Flags())
componentCmd.AddCommand(componentGetCmd)
componentCmd.AddCommand(componentSetCmd)
//Adding `--project` flag
addProjectFlag(componentGetCmd)
addProjectFlag(componentSetCmd)
//Adding `--application` flag
addApplicationFlag(componentGetCmd)
addApplicationFlag(componentSetCmd)
// Add a defined annotation in order to appear in the help menu
componentCmd.Annotations = map[string]string{"command": "component"}
componentCmd.SetUsageTemplate(cmdUsageTemplate)
rootCmd.AddCommand(componentCmd)
}

View File

@@ -1,98 +0,0 @@
package cmd
import (
"fmt"
"os"
"strings"
"text/tabwriter"
"github.com/pkg/errors"
"github.com/redhat-developer/odo/pkg/config"
"github.com/spf13/cobra"
)
// configurationCmd represents the app command
var configurationCmd = &cobra.Command{
Use: "config",
Short: "Modifies configuration settings",
Long: `Modifies Odo specific configuration settings within the config file.
Available Parameters:
UpdateNotification - Controls if an update notification is shown or not (true or false)
NamePrefix - Default prefix is the current directory name. Use this value to set a default name prefix
Timeout - Timeout (in seconds) for OpenShift server connection check`,
Example: fmt.Sprintf("%s\n%s\n",
configurationViewCmd.Example,
configurationSetCmd.Example),
Aliases: []string{"configuration"},
// 'odo utils config' is the same as 'odo utils config --help'
Args: func(cmd *cobra.Command, args []string) error {
if len(args) >= 1 && args[0] != "view" && args[0] != "set" {
return fmt.Errorf(`Unknown command, use "set" or "view"`)
}
return nil
}, Run: func(cmd *cobra.Command, args []string) {
if len(args) > 0 && args[0] == "set" {
configurationSetCmd.Run(cmd, args)
} else if len(args) > 0 && args[0] == "view" {
configurationViewCmd.Run(cmd, args)
} else {
cmd.Help()
}
},
}
var configurationSetCmd = &cobra.Command{
Use: "set",
Short: "Set a value in odo config file",
Long: `Set an individual value in the Odo configuration file
Available Parameters:
UpdateNotification - Controls if an update notification is shown or not (true or false)
NamePrefix - Default prefix is the current directory name. Use this value to set a default name prefix.
Timeout - Timeout (in seconds) for OpenShift server connection check`,
Example: `
# Set a configuration value
odo utils config set UpdateNotification false
odo utils config set NamePrefix "app"
odo utils config set timeout 20
`,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 2 {
return fmt.Errorf("Please provide a parameter name and value")
} else if len(args) > 2 {
return fmt.Errorf("Only one value per parameter is allowed")
} else {
return nil
}
}, RunE: func(cmd *cobra.Command, args []string) error {
cfg, err := config.New()
if err != nil {
return errors.Wrapf(err, "unable to set configuration")
}
return cfg.SetConfiguration(strings.ToLower(args[0]), args[1])
},
}
var configurationViewCmd = &cobra.Command{
Use: "view",
Short: "View current configuration values",
Long: "View current configuration values",
Example: ` # For viewing the current configuration
odo utils config view
`,
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
cfg, err := config.New()
if err != nil {
fmt.Println(err, ": unable to view configuration")
}
w := tabwriter.NewWriter(os.Stdout, 5, 2, 2, ' ', tabwriter.TabIndent)
fmt.Fprintln(w, "PARAMETER", "\t", "CURRENT_VALUE")
fmt.Fprintln(w, "UpdateNotification", "\t", cfg.GetUpdateNotification())
fmt.Fprintln(w, "NamePrefix", "\t", cfg.GetNamePrefix())
fmt.Fprintln(w, "Timeout", "\t", cfg.GetTimeout())
w.Flush()
},
}

View File

@@ -1,240 +0,0 @@
package cmd
import (
"fmt"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
"github.com/redhat-developer/odo/pkg/odo/util/completion"
"os"
"path/filepath"
"strings"
"github.com/fatih/color"
"github.com/golang/glog"
"github.com/redhat-developer/odo/pkg/application"
"github.com/redhat-developer/odo/pkg/catalog"
"github.com/redhat-developer/odo/pkg/component"
"github.com/redhat-developer/odo/pkg/util"
"github.com/spf13/cobra"
)
var (
componentBinary string
componentGit string
componentLocal string
componentPorts []string
componentEnvVars []string
)
var componentCreateCmd = &cobra.Command{
Use: "create <component_type> [component_name] [flags]",
Short: "Create a new component",
Long: `Create a new component to deploy on OpenShift.
If a component name is not provided, it'll be auto-generated.
By default, builder images will be used from the current namespace. You can explicitly supply a namespace by using: odo create namespace/name:version
If version is not specified by default, latest wil be chosen as the version.
A full list of component types that can be deployed is available using: 'odo catalog list'`,
Example: ` # Create new Node.js component with the source in current directory.
odo create nodejs
# A specific image version may also be specified
odo create nodejs:latest
# Create new Node.js component named 'frontend' with the source in './frontend' directory
odo create nodejs frontend --local ./frontend
# Create new Node.js component with source from remote git repository.
odo create nodejs --git https://github.com/openshift/nodejs-ex.git
# Create a new Node.js component of version 6 from the 'openshift' namespace
odo create openshift/nodejs:6 --local /nodejs-ex
# Create new Wildfly component with binary named sample.war in './downloads' directory
odo create wildfly wildly --binary ./downloads/sample.war
# Create new Node.js component with the source in current directory and ports 8080-tcp,8100-tcp and 9100-udp exposed
odo create nodejs --port 8080,8100/tcp,9100/udp
# Create new Node.js component with the source in current directory and env variables key=value and key1=value1 exposed
odo create nodejs --env key=value,key1=value1
# For more examples, visit: https://github.com/redhat-developer/odo/blob/master/docs/examples.md
odo create python --git https://github.com/openshift/django-ex.git
`,
Args: cobra.RangeArgs(1, 2),
Run: func(cmd *cobra.Command, args []string) {
stdout := color.Output
glog.V(4).Infof("Component create called with args: %#v, flags: binary=%s, git=%s, local=%s", strings.Join(args, " "), componentBinary, componentGit, componentLocal)
context := genericclioptions.NewContextCreatingAppIfNeeded(cmd)
client := context.Client
projectName := context.Project
applicationName := context.Application
checkFlag := 0
componentPath := ""
var componentPathType component.CreateType
if len(componentBinary) != 0 {
componentPath = componentBinary
componentPathType = component.BINARY
checkFlag++
}
if len(componentGit) != 0 {
componentPath = componentGit
componentPathType = component.GIT
checkFlag++
}
if len(componentLocal) != 0 {
componentPath = componentLocal
componentPathType = component.SOURCE
checkFlag++
}
if checkFlag > 1 {
fmt.Println("The source can be either --binary or --local or --git")
os.Exit(1)
}
componentImageName, componentType, _, componentVersion := util.ParseCreateCmdArgs(args)
// Fetch list of existing components in-order to attempt generation of unique component name
componentList, err := component.List(client, applicationName)
odoutil.CheckError(err, "")
// Generate unique name for component
componentName, err := component.GetDefaultComponentName(
componentPath,
componentPathType,
componentType,
componentList,
)
odoutil.CheckError(err, "")
// Check to see if the catalog type actually exists
exists, err := catalog.Exists(client, componentType)
odoutil.CheckError(err, "")
if !exists {
fmt.Printf("Invalid component type: %v\nRun 'odo catalog list components' to see a list of supported component types\n", componentType)
os.Exit(1)
}
// Check to see if that particular version exists
versionExists, err := catalog.VersionExists(client, componentType, componentVersion)
odoutil.CheckError(err, "")
if !versionExists {
fmt.Printf("Invalid component version: %v\nRun 'odo catalog list components' to see a list of supported component type versions\n", componentVersion)
os.Exit(1)
}
// Retrieve the componentName, if the componentName isn't specified, we will use the default image name
if len(args) == 2 {
componentName = args[1]
}
// Validate component name
err = validateName(componentName)
odoutil.CheckError(err, "")
exists, err = component.Exists(client, componentName, applicationName)
odoutil.CheckError(err, "")
if exists {
fmt.Printf("component with the name %s already exists in the current application\n", componentName)
os.Exit(1)
}
// Deploy the component with Git
if len(componentGit) != 0 {
// Use Git
err := component.CreateFromGit(client, componentName, componentImageName, componentGit, applicationName, componentPorts, componentEnvVars)
odoutil.CheckError(err, "")
fmt.Printf("Triggering build from %s.\n\n", componentGit)
// Git is the only one using BuildConfig since we need to retrieve the git
err = component.Build(client, componentName, applicationName, true, true, stdout)
odoutil.CheckError(err, "")
} else if len(componentLocal) != 0 {
// Use the absolute path for the component
dir, err := filepath.Abs(componentLocal)
odoutil.CheckError(err, "")
fileInfo, err := os.Stat(dir)
odoutil.CheckError(err, "")
if !fileInfo.IsDir() {
fmt.Println("Please provide a path to the directory")
os.Exit(1)
}
// Create
err = component.CreateFromPath(client, componentName, componentImageName, dir, applicationName, "local", componentPorts, componentEnvVars)
odoutil.CheckError(err, "")
} else if len(componentBinary) != 0 {
// Deploy the component with a binary
// Retrieve the path of the binary
path, err := filepath.Abs(componentBinary)
odoutil.CheckError(err, "")
// Create
err = component.CreateFromPath(client, componentName, componentImageName, path, applicationName, "binary", componentPorts, componentEnvVars)
odoutil.CheckError(err, "")
} else {
// If the user does not provide anything (local, git or binary), use the current absolute path and deploy it
dir, err := filepath.Abs("./")
odoutil.CheckError(err, "")
// Create
err = component.CreateFromPath(client, componentName, componentImageName, dir, applicationName, "local", componentPorts, componentEnvVars)
odoutil.CheckError(err, "")
}
ports, err := component.GetComponentPorts(client, componentName, applicationName)
odoutil.CheckError(err, "")
fmt.Printf("Component '%s' was created", componentName)
if len(ports) > 1 {
fmt.Printf(" and ports %s were opened\n", strings.Join(ports, ","))
} else if len(ports) == 1 {
fmt.Printf(" and port %s was opened\n", ports[0])
}
if len(componentGit) == 0 {
fmt.Printf("To push source code to the component run 'odo push'\n")
}
// after component is successfully created, set is as active
err = application.SetCurrent(client, applicationName)
odoutil.CheckError(err, "")
err = component.SetCurrent(componentName, applicationName, projectName)
odoutil.CheckError(err, "")
fmt.Printf("\nComponent '%s' is now set as active component.\n", componentName)
},
}
func init() {
componentCreateCmd.Flags().StringVarP(&componentBinary, "binary", "b", "", "Use a binary as the source file for the component")
componentCreateCmd.Flags().StringVarP(&componentGit, "git", "g", "", "Use a git repository as the source file for the component")
componentCreateCmd.Flags().StringVarP(&componentLocal, "local", "l", "", "Use local directory as a source file for the component")
componentCreateCmd.Flags().StringSliceVarP(&componentPorts, "port", "p", []string{}, "Ports to be used when the component is created (ex. 8080,8100/tcp,9100/udp")
componentCreateCmd.Flags().StringSliceVar(&componentEnvVars, "env", []string{}, "Environmental variables for the component. For example --env VariableName=Value")
// Add a defined annotation in order to appear in the help menu
componentCreateCmd.Annotations = map[string]string{"command": "component"}
componentCreateCmd.SetUsageTemplate(cmdUsageTemplate)
//Adding `--project` flag
addProjectFlag(componentCreateCmd)
//Adding `--application` flag
addApplicationFlag(componentCreateCmd)
completion.RegisterCommandFlagHandler(componentCreateCmd, "local", completion.FileCompletionHandler)
completion.RegisterCommandFlagHandler(componentCreateCmd, "binary", completion.FileCompletionHandler)
rootCmd.AddCommand(componentCreateCmd)
}

View File

@@ -1,86 +0,0 @@
package cmd
import (
"fmt"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util"
"strings"
"github.com/golang/glog"
"github.com/redhat-developer/odo/pkg/component"
"github.com/spf13/cobra"
)
var (
componentShortFlag bool
componentForceDeleteFlag bool
)
var componentDeleteCmd = &cobra.Command{
Use: "delete <component_name>",
Short: "Delete an existing component",
Long: "Delete an existing component.",
Example: ` # Delete component named 'frontend'.
odo delete frontend
`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
glog.V(4).Infof("component delete called")
glog.V(4).Infof("args: %#v", strings.Join(args, " "))
context := genericclioptions.NewContext(cmd)
client := context.Client
projectName := context.Project
applicationName := context.Application
// If no arguments have been passed, get the current component
// else, use the first argument and check to see if it exists
var componentName string
if len(args) == 0 {
componentName = context.Component()
} else {
componentName = context.Component(args[0])
}
var confirmDeletion string
if componentForceDeleteFlag {
confirmDeletion = "y"
} else {
fmt.Printf("Are you sure you want to delete %v from %v? [y/N] ", componentName, applicationName)
fmt.Scanln(&confirmDeletion)
}
if strings.ToLower(confirmDeletion) == "y" {
err := component.Delete(client, componentName, applicationName)
util.CheckError(err, "")
fmt.Printf("Component %s from application %s has been deleted\n", componentName, applicationName)
currentComponent, err := component.GetCurrent(applicationName, projectName)
util.CheckError(err, "Unable to get current component")
if currentComponent == "" {
fmt.Println("No default component has been set")
} else {
fmt.Printf("Default component set to: %s\n", currentComponent)
}
} else {
fmt.Printf("Aborting deletion of component: %v\n", componentName)
}
},
}
func init() {
componentDeleteCmd.Flags().BoolVarP(&componentForceDeleteFlag, "force", "f", false, "Delete component without prompting")
// Add a defined annotation in order to appear in the help menu
componentDeleteCmd.Annotations = map[string]string{"command": "component"}
componentDeleteCmd.SetUsageTemplate(cmdUsageTemplate)
//Adding `--project` flag
addProjectFlag(componentDeleteCmd)
//Adding `--application` flag
addApplicationFlag(componentDeleteCmd)
rootCmd.AddCommand(componentDeleteCmd)
}

View File

@@ -1,48 +0,0 @@
package cmd
import (
"github.com/redhat-developer/odo/pkg/component"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util"
"github.com/spf13/cobra"
)
var describeCmd = &cobra.Command{
Use: "describe [component_name]",
Short: "Describe the given component",
Long: `Describe the given component.`,
Example: ` # Describe nodejs component,
odo describe nodejs
`,
Args: cobra.RangeArgs(0, 1),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
applicationName := context.Application
// If no arguments have been passed, get the current component
// else, use the first argument and check to see if it exists
var componentName string
if len(args) == 0 {
componentName = context.Component()
} else {
componentName = context.Component(args[0])
}
componentType, path, componentURL, appStore, err := component.GetComponentDesc(client, componentName, applicationName)
util.CheckError(err, "")
printComponentInfo(componentName, componentType, path, componentURL, appStore)
},
}
func init() {
// Add a defined annotation in order to appear in the help menu
describeCmd.Annotations = map[string]string{"command": "component"}
describeCmd.SetUsageTemplate(cmdUsageTemplate)
//Adding `--project` flag
addProjectFlag(describeCmd)
//Adding `--application` flag
addApplicationFlag(describeCmd)
rootCmd.AddCommand(describeCmd)
}

View File

@@ -1,155 +0,0 @@
package cmd
import (
"fmt"
"github.com/golang/glog"
"github.com/redhat-developer/odo/pkg/component"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util"
"github.com/redhat-developer/odo/pkg/secret"
"os"
svc "github.com/redhat-developer/odo/pkg/service"
"github.com/spf13/cobra"
)
var (
port string
wait bool
)
var linkCmd = &cobra.Command{
Use: "link <service> --component [component] OR link <component> --component [component]",
Short: "Link component to a service or component",
Long: `Link component to a service or component
If the source component is not provided, the current active component is assumed.
In both use cases, link adds the appropriate secret to the environment of the source component.
The source component can then consume the entries of the secret as environment variables.
For example:
We have created a frontend application called 'frontend':
odo create nodejs frontend
We've also created a backend application called 'backend' with port 8080 exposed:
odo create nodejs backend --port 8080
You can now link the two applications:
odo link backend --component frontend
Now the frontend has 2 ENV variables it can use:
COMPONENT_BACKEND_HOST=backend-app
COMPONENT_BACKEND_PORT=8080
If you wish to use a database, we can use the Service Catalog and link it to our backend:
odo service create dh-postgresql-apb --plan dev -p postgresql_user=luke -p postgresql_password=secret
odo link dh-postgresql-apb
Now backend has 2 ENV variables it can use:
DB_USER=luke
DB_PASSWORD=secret
`,
Example: ` # Link the current component to the 'my-postgresql' service
odo link my-postgresql
# Link component 'nodejs' to the 'my-postgresql' service
odo link my-postgresql --component nodejs
# Link current component to the 'backend' component (backend must have a single exposed port)
odo link backend
# Link component 'nodejs' to the 'backend' component
odo link backend --component nodejs
# Link current component to port 8080 of the 'backend' component (backend must have port 8080 exposed)
odo link backend --port 8080
`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
projectName := context.Project
applicationName := context.Application
sourceComponentName := context.Component()
suppliedName := args[0]
svcSxists, err := svc.SvcExists(client, suppliedName, applicationName)
util.CheckError(err, "Unable to determine if service %s exists", suppliedName)
cmpExists, err := component.Exists(client, suppliedName, applicationName)
util.CheckError(err, "Unable to determine if component %s exists", suppliedName)
if svcSxists {
if cmpExists {
glog.V(4).Infof("Both a service and component with name %s - assuming a link to the service is required", suppliedName)
}
serviceName := suppliedName
// if there is a ServiceBinding, then that means there is already a secret (or there will be soon)
// which we can link to
_, err = client.GetServiceBinding(serviceName, projectName)
if err != nil {
fmt.Printf(`The service was not created via Odo.
Please delete the service and recreate it using 'odo service create %s'`, serviceName)
os.Exit(1)
}
if wait {
// we wait until the secret has been created on the OpenShift
// this is done because the secret is only created after the Pod that runs the
// service is in running state.
// This can take a long time to occur if the image of the service has yet to be downloaded
fmt.Printf("Waiting for secret of service %s to come up\n", serviceName)
_, err = client.WaitAndGetSecret(serviceName, projectName)
util.CheckError(err, "")
} else {
// we also need to check whether there is a secret with the same name as the service
// the secret should have been created along with the secret
_, err = client.GetSecret(serviceName, projectName)
if err != nil {
fmt.Printf(`The service %s created by 'odo service create' is being provisioned.
You may have to wait a few seconds until OpenShift fully provisions it.`, serviceName)
os.Exit(1)
}
}
err = client.LinkSecret(serviceName, sourceComponentName, applicationName, projectName)
util.CheckError(err, "")
fmt.Printf("Service %s has been successfully linked to the component %s.\n", serviceName, sourceComponentName)
} else if cmpExists {
targetComponent := args[0]
secretName, err := secret.DetermineSecretName(client, targetComponent, applicationName, port)
util.CheckError(err, "")
err = client.LinkSecret(secretName, sourceComponentName, applicationName, projectName)
util.CheckError(err, "")
fmt.Printf("Component %s has been successfully linked to component %s.\n", targetComponent, sourceComponentName)
} else {
fmt.Printf(`Neither a service nor a component named %s could be located
Please create one of the two before attempting to use odo link`, suppliedName)
os.Exit(1)
}
},
}
func init() {
linkCmd.PersistentFlags().StringVar(&port, "port", "", "Port of the backend to which to link")
linkCmd.PersistentFlags().BoolVarP(&wait, "wait", "w", false, "If enabled, the link command will wait for the service to be provisioned")
// Add a defined annotation in order to appear in the help menu
linkCmd.Annotations = map[string]string{"command": "component"}
linkCmd.SetUsageTemplate(cmdUsageTemplate)
//Adding `--project` flag
addProjectFlag(linkCmd)
//Adding `--application` flag
addApplicationFlag(linkCmd)
//Adding `--component` flag
addComponentFlag(linkCmd)
rootCmd.AddCommand(linkCmd)
}

View File

@@ -1,60 +0,0 @@
package cmd
import (
"fmt"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util"
"os"
"text/tabwriter"
"github.com/redhat-developer/odo/pkg/component"
"github.com/spf13/cobra"
)
var componentListCmd = &cobra.Command{
Use: "list",
Short: "List all components in the current application",
Long: "List all components in the current application.",
Example: ` # List all components in the application
odo list
`,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
applicationName := context.Application
components, err := component.List(client, applicationName)
util.CheckError(err, "")
if len(components) == 0 {
fmt.Println("There are no components deployed.")
return
}
activeMark := " "
w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
fmt.Fprintln(w, "ACTIVE", "\t", "NAME", "\t", "TYPE")
currentComponent := context.ComponentAllowingEmpty(true)
for _, comp := range components {
if comp.Name == currentComponent {
activeMark = "*"
}
fmt.Fprintln(w, activeMark, "\t", comp.Name, "\t", comp.Type)
activeMark = " "
}
w.Flush()
},
}
func init() {
// Add a defined annotation in order to appear in the help menu
componentListCmd.Annotations = map[string]string{"command": "component"}
//Adding `--project` flag
addProjectFlag(componentListCmd)
//Adding `--application` flag
addApplicationFlag(componentListCmd)
rootCmd.AddCommand(componentListCmd)
}

View File

@@ -1,58 +0,0 @@
package cmd
import (
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util"
"os"
"github.com/redhat-developer/odo/pkg/component"
"github.com/spf13/cobra"
)
var (
logFollow bool
)
var logCmd = &cobra.Command{
Use: "log [component_name]",
Short: "Retrieve the log for the given component.",
Long: `Retrieve the log for the given component.`,
Example: ` # Get the logs for the nodejs component
odo log nodejs
`,
Args: cobra.RangeArgs(0, 1),
Run: func(cmd *cobra.Command, args []string) {
// Retrieve stdout / io.Writer
stdout := os.Stdout
context := genericclioptions.NewContext(cmd)
client := context.Client
applicationName := context.Application
var argComponent string
if len(args) == 1 {
argComponent = args[0]
}
componentName := context.Component(argComponent)
// Retrieve the log
err := component.GetLogs(client, componentName, applicationName, logFollow, stdout)
util.CheckError(err, "Unable to retrieve logs, does your component exist?")
},
}
func init() {
logCmd.Flags().BoolVarP(&logFollow, "follow", "f", false, "Follow logs")
// Add a defined annotation in order to appear in the help menu
logCmd.Annotations = map[string]string{"command": "component"}
logCmd.SetUsageTemplate(cmdUsageTemplate)
//Adding `--project` flag
addProjectFlag(logCmd)
//Adding `--application` flag
addApplicationFlag(logCmd)
rootCmd.AddCommand(logCmd)
}

View File

@@ -1,58 +0,0 @@
package cmd
import (
"github.com/redhat-developer/odo/pkg/auth"
"github.com/redhat-developer/odo/pkg/odo/util"
"github.com/spf13/cobra"
)
var (
userName string
password string
token string
caAuth string
skipTLS bool
)
// versionCmd represents the version command
var loginCmd = &cobra.Command{
Use: "login",
Short: "Login to cluster",
Long: "Login to cluster",
Example: `
# Log in interactively
odo login
# Log in to the given server with the given certificate authority file
odo login localhost:8443 --certificate-authority=/path/to/cert.crt
# Log in to the given server with the given credentials (basic auth)
odo login localhost:8443 --username=myuser --password=mypass
# Log in to the given server with the given credentials (token)
odo login localhost:8443 --token=xxxxxxxxxxxxxxxxxxxxxxx
`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
var server string
if len(args) == 1 {
server = args[0]
}
err := auth.Login(server, userName, password, token, caAuth, skipTLS)
if err != nil {
util.CheckError(err, "")
}
},
}
func init() {
// Add a defined annotation in order to appear in the help menu
loginCmd.Annotations = map[string]string{"command": "utility"}
loginCmd.SetUsageTemplate(cmdUsageTemplate)
loginCmd.Flags().StringVarP(&userName, "username", "u", userName, "username, will prompt if not provided")
loginCmd.Flags().StringVarP(&password, "password", "p", password, "password, will prompt if not provided")
loginCmd.Flags().StringVarP(&token, "token", "t", token, "token, will prompt if not provided")
loginCmd.Flags().BoolVar(&skipTLS, "insecure-skip-tls-verify", false, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure")
loginCmd.Flags().StringVar(&caAuth, "certificate-authority", userName, "Path to a cert file for the certificate authority")
rootCmd.AddCommand(loginCmd)
}

View File

@@ -1,32 +0,0 @@
package cmd
import (
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util"
"github.com/spf13/cobra"
"os"
)
var logoutCmd = &cobra.Command{
Use: "logout",
Short: "Log out of the current OpenShift session",
Long: "Log out of the current OpenShift session",
Example: ` # Logout
odo logout
`,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
err := client.RunLogout(os.Stdout)
util.CheckError(err, "")
},
}
func init() {
// Add a defined annotation in order to appear in the help menu
logoutCmd.Annotations = map[string]string{"command": "utility"}
logoutCmd.SetUsageTemplate(cmdUsageTemplate)
rootCmd.AddCommand(logoutCmd)
}

85
cmd/odo/odo.go Normal file
View File

@@ -0,0 +1,85 @@
package main
import (
"flag"
"github.com/posener/complete"
"github.com/redhat-developer/odo/pkg/odo/cli"
"github.com/redhat-developer/odo/pkg/odo/util/completion"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
func main() {
// create the complete command
root := cli.RootCmd()
rootCmp := createCompletion(root)
cmp := complete.New("odo", rootCmp)
// AddFlags adds the completion flags to the program flags, specifying custom names
cmp.CLI.InstallName = "complete"
cmp.CLI.UninstallName = "uncomplete"
cmp.AddFlags(nil)
// add the completion flags to the root command, though they won't appear in completions
root.Flags().AddGoFlagSet(flag.CommandLine)
// override usage so that flag.Parse uses root command's usage instead of default one when invoked with -h
flag.Usage = usage
// parse the flags - both the program's flags and the completion flags
flag.Parse()
// run the completion, in case that the completion was invoked
// and ran as a completion script or handled a flag that passed
// as argument, the Run method will return true,
// in that case, our program have nothing to do and should return.
if cmp.Complete() {
return
}
// Call commands
cli.Execute()
}
func usage() {
_ = cli.RootCmd().Usage()
}
func createCompletion(root *cobra.Command) complete.Command {
rootCmp := complete.Command{}
rootCmp.Flags = make(complete.Flags)
addFlags := func(flag *pflag.Flag) {
if flag.Hidden {
return
}
var handler complete.Predictor
handler, ok := completion.GetCommandFlagHandler(root, flag.Name)
if !ok {
handler = complete.PredictAnything
}
if len(flag.Shorthand) > 0 {
rootCmp.Flags["-"+flag.Shorthand] = handler
}
rootCmp.Flags["--"+flag.Name] = handler
}
root.LocalFlags().VisitAll(addFlags)
root.InheritedFlags().VisitAll(addFlags)
if root.HasAvailableSubCommands() {
rootCmp.Sub = make(complete.Commands)
for _, c := range root.Commands() {
if !c.Hidden {
rootCmp.Sub[c.Name()] = createCompletion(c)
}
}
}
var handler complete.Predictor
handler, ok := completion.GetCommandHandler(root)
if !ok {
handler = complete.PredictNothing
}
rootCmp.Args = handler
return rootCmp
}

View File

@@ -1,221 +0,0 @@
package cmd
import (
"fmt"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util"
"github.com/redhat-developer/odo/pkg/odo/util/completion"
"os"
"strings"
"github.com/redhat-developer/odo/pkg/project"
"github.com/spf13/cobra"
)
var (
projectShortFlag bool
projectForceDeleteFlag bool
)
var projectCmd = &cobra.Command{
Use: "project [options]",
Short: "Perform project operations",
Long: "Perform project operations",
Example: fmt.Sprintf("%s\n%s\n%s\n%s\n%s",
projectSetCmd.Example,
projectCreateCmd.Example,
projectListCmd.Example,
projectDeleteCmd.Example,
projectGetCmd.Example),
// 'odo project' is the same as 'odo project get'
// 'odo project <project_name>' is the same as 'odo project set <project_name>'
Run: func(cmd *cobra.Command, args []string) {
if len(args) > 0 && args[0] != "get" && args[0] != "set" {
projectSetCmd.Run(cmd, args)
} else {
projectGetCmd.Run(cmd, args)
}
},
}
var projectSetCmd = &cobra.Command{
Use: "set",
Short: "Set the current active project",
Long: "Set the current active project",
Example: ` # Set the current active project
odo project set myproject
`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
projectName := args[0]
context := genericclioptions.NewContext(cmd)
client := context.Client
current := context.Project
exists, err := project.Exists(client, projectName)
util.CheckError(err, "")
if !exists {
fmt.Printf("The project %s does not exist\n", projectName)
os.Exit(1)
}
err = project.SetCurrent(client, projectName)
util.CheckError(err, "")
if projectShortFlag {
fmt.Print(projectName)
} else {
if current == projectName {
fmt.Printf("Already on project : %v\n", projectName)
} else {
fmt.Printf("Switched to project : %v\n", projectName)
}
}
},
}
var projectGetCmd = &cobra.Command{
Use: "get",
Short: "Get the active project",
Long: "Get the active project",
Example: ` # Get the active project
odo project get
`,
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
project := context.Project
if projectShortFlag {
fmt.Print(project)
} else {
fmt.Printf("The current project is: %v\n", project)
}
},
}
var projectCreateCmd = &cobra.Command{
Use: "create",
Short: "Create a new project",
Long: "Create a new project",
Example: ` # Create a new project
odo project create myproject
`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
projectName := args[0]
client := genericclioptions.Client(cmd)
err := project.Create(client, projectName)
util.CheckError(err, "")
err = project.SetCurrent(client, projectName)
util.CheckError(err, "")
fmt.Printf("New project created and now using project : %v\n", projectName)
},
}
var projectDeleteCmd = &cobra.Command{
Use: "delete",
Short: "Delete a project",
Long: "Delete a project and all resources deployed in the project being deleted",
Example: ` # Delete a project
odo project delete myproject
`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
projectName := args[0]
client := genericclioptions.Client(cmd)
// Validate existence of the project to be deleted
isValidProject, err := project.Exists(client, projectName)
util.CheckError(err, "Failed to delete project %s", projectName)
if !isValidProject {
fmt.Printf("The project %s does not exist. Please check the list of projects using `odo project list`\n", projectName)
os.Exit(1)
}
var confirmDeletion string
if projectForceDeleteFlag {
confirmDeletion = "y"
} else {
fmt.Printf("Are you sure you want to delete project %v? [y/N] ", projectName)
fmt.Scanln(&confirmDeletion)
}
if strings.ToLower(confirmDeletion) != "y" {
fmt.Printf("Aborting deletion of project: %v\n", projectName)
os.Exit(1)
}
fmt.Printf("Deleting project %s...\n(this operation may take some time)\n", projectName)
err = project.Delete(client, projectName)
if err != nil {
util.CheckError(err, "")
}
fmt.Printf("Deleted project : %v\n", projectName)
// Get Current Project
currProject := project.GetCurrent(client)
// Check if List returns empty, if so, the currProject is showing old currentProject
// In openshift, when the project is deleted, it does not reset the current project in kube config file which is used by odo for current project
projects, err := project.List(client)
util.CheckError(err, "")
if len(projects) != 0 {
fmt.Printf("%s has been set as the active project\n", currProject)
} else {
// oc errors out as "error: you do not have rights to view project "$deleted_project"."
fmt.Printf("You are not a member of any projects. You can request a project to be created using the `odo project create <project_name>` command\n")
}
},
}
var projectListCmd = &cobra.Command{
Use: "list",
Short: "List all the projects",
Long: "List all the projects",
Example: ` # List all the projects
odo project list
`,
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
client := genericclioptions.Client(cmd)
projects, err := project.List(client)
util.CheckError(err, "")
if len(projects) == 0 {
fmt.Println("You are not a member of any projects. You can request a project to be created using the `odo project create <project_name>` command")
return
}
fmt.Printf("ACTIVE NAME\n")
for _, project := range projects {
activeMark := " "
if project.Active {
activeMark = "*"
}
fmt.Printf(" %s %s\n", activeMark, project.Name)
}
},
}
func init() {
projectGetCmd.Flags().BoolVarP(&projectShortFlag, "short", "q", false, "If true, display only the project name")
projectSetCmd.Flags().BoolVarP(&projectShortFlag, "short", "q", false, "If true, display only the project name")
projectDeleteCmd.Flags().BoolVarP(&projectForceDeleteFlag, "force", "f", false, "Delete project without prompting")
projectDeleteCmd.Flags().BoolVarP(&projectShortFlag, "short", "q", false, "Delete project without prompting")
projectCmd.Flags().AddFlagSet(projectGetCmd.Flags())
projectCmd.AddCommand(projectGetCmd)
projectCmd.AddCommand(projectSetCmd)
projectCmd.AddCommand(projectCreateCmd)
projectCmd.AddCommand(projectDeleteCmd)
projectCmd.AddCommand(projectListCmd)
// Add a defined annotation in order to appear in the help menu
projectCmd.Annotations = map[string]string{"command": "other"}
projectCmd.SetUsageTemplate(cmdUsageTemplate)
completion.RegisterCommandHandler(projectSetCmd, completion.ProjectNameCompletionHandler)
completion.RegisterCommandHandler(projectDeleteCmd, completion.ProjectNameCompletionHandler)
rootCmd.AddCommand(projectCmd)
}

View File

@@ -1,116 +0,0 @@
package cmd
import (
"fmt"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
"net/url"
"os"
"runtime"
"github.com/fatih/color"
"github.com/redhat-developer/odo/pkg/component"
"github.com/redhat-developer/odo/pkg/util"
"path/filepath"
"github.com/golang/glog"
"github.com/spf13/cobra"
)
var pushCmd = &cobra.Command{
Use: "push [component name]",
Short: "Push source code to a component",
Long: `Push source code to a component.`,
Example: ` # Push source code to the current component
odo push
# Push data to the current component from the original source.
odo push
# Push source code in ~/mycode to component called my-component
odo push my-component --local ~/mycode
`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
stdout := color.Output
context := genericclioptions.NewContext(cmd)
client := context.Client
applicationName := context.Application
var argComponent string
if len(args) == 1 {
argComponent = args[0]
}
componentName := context.Component(argComponent)
fmt.Printf("Pushing changes to component: %v\n", componentName)
sourceType, sourcePath, err := component.GetComponentSource(client, componentName, applicationName)
odoutil.CheckError(err, "unable to get component source")
switch sourceType {
case "local", "binary":
// use value of '--dir' as source if it was used
if len(componentLocal) != 0 {
if sourceType == "binary" {
fmt.Printf("Unable to push local directory:%s to component %s that uses binary %s.\n", componentLocal, componentName, sourcePath)
os.Exit(1)
}
sourcePath = util.GenFileUrl(componentLocal, runtime.GOOS)
}
u, err := url.Parse(sourcePath)
odoutil.CheckError(err, fmt.Sprintf("unable to parse source %s from component %s", sourcePath, componentName))
if u.Scheme != "" && u.Scheme != "file" {
fmt.Printf("Component %s has invalid source path %s", componentName, u.Scheme)
os.Exit(1)
}
localLocation := util.ReadFilePath(u, runtime.GOOS)
_, err = os.Stat(localLocation)
if err != nil {
odoutil.CheckError(err, "")
}
if sourceType == "local" {
glog.V(4).Infof("Copying directory %s to pod", localLocation)
err = component.PushLocal(client, componentName, applicationName, localLocation, os.Stdout, []string{})
} else {
dir := filepath.Dir(localLocation)
glog.V(4).Infof("Copying file %s to pod", localLocation)
err = component.PushLocal(client, componentName, applicationName, dir, os.Stdout, []string{localLocation})
}
odoutil.CheckError(err, fmt.Sprintf("failed to push component: %v", componentName))
case "git":
// currently we don't support changing build type
// it doesn't make sense to use --dir with git build
if len(componentLocal) != 0 {
fmt.Printf("Unable to push local directory:%s to component %s that uses Git repository:%s.\n", componentLocal, componentName, sourcePath)
os.Exit(1)
}
err := component.Build(client, componentName, applicationName, true, true, stdout)
odoutil.CheckError(err, fmt.Sprintf("failed to push component: %v", componentName))
}
fmt.Printf("changes successfully pushed to component: %v\n", componentName)
},
}
func init() {
pushCmd.Flags().StringVarP(&componentLocal, "local", "l", "", "Use given local directory as a source for component. (It must be a local component)")
// Add a defined annotation in order to appear in the help menu
pushCmd.Annotations = map[string]string{"command": "component"}
pushCmd.SetUsageTemplate(cmdUsageTemplate)
//Adding `--project` flag
addProjectFlag(pushCmd)
//Adding `--application` flag
addApplicationFlag(pushCmd)
rootCmd.AddCommand(pushCmd)
}

View File

@@ -1,156 +0,0 @@
package cmd
import (
"flag"
"fmt"
"github.com/golang/glog"
"github.com/redhat-developer/odo/pkg/config"
"github.com/redhat-developer/odo/pkg/notify"
"github.com/redhat-developer/odo/pkg/odo/util"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
// Templates
var rootUsageTemplate = `Usage:{{if .Runnable}}
{{if .HasAvailableFlags}}{{appendIfNotPresent .UseLine "[flags]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
{{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}}
Aliases:
{{.NameAndAliases}}{{end}}{{if .HasExample}}
Examples:
{{ .Example }}{{end}}{{ if .HasAvailableSubCommands}}
Component Commands:{{range .Commands}}{{if eq .Annotations.command "component"}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableLocalFlags}}
Other Commands:{{range .Commands}}{{if eq .Annotations.command "other"}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableLocalFlags}}
Utility Commands:{{range .Commands}}{{if or (eq .Annotations.command "utility") (eq .Name "help") }}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableLocalFlags}}
Flags:
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableInheritedFlags}}
Global Flags:
{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsHelpCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableSubCommands }}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
var cmdUsageTemplate = `Usage:{{if .Runnable}}
{{if .HasAvailableFlags}}{{appendIfNotPresent .UseLine "[flags]"}}{{else}}{{.UseLine}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
{{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}}
Aliases:
{{.NameAndAliases}}{{end}}{{if .HasExample}}
Examples:
{{ .Example }}{{end}}{{ if .HasAvailableSubCommands}}
Available Commands:{{range .Commands}}{{if .IsAvailableCommand}}
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableLocalFlags}}
Flags:
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableInheritedFlags}}
Global Flags:
{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}}
Additional help topics:{{range .Commands}}{{if .IsHelpCommand}}
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableSubCommands }}
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: util.RootCommandName,
Short: "Odo (Openshift Do)",
Long: `Odo (OpenShift Do) is a CLI tool for running OpenShift applications in a fast and automated matter. Odo reduces the complexity of deployment by adding iterative development without the worry of deploying your source code.
Find more information at https://github.com/redhat-developer/odo`,
Example: ` # Creating and deploying a Node.js project
git clone https://github.com/openshift/nodejs-ex && cd nodejs-ex
odo create nodejs
odo push
# Accessing your Node.js component
odo url create`,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
},
}
// RootCmd exposes the root command to main package to allow inspection by completion code
func RootCmd() *cobra.Command {
return rootCmd
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
// checking the value of updatenotification in config
// before proceeding with fetching the latest version
cfg, err := config.New()
if err != nil {
util.CheckError(err, "")
}
if cfg.GetUpdateNotification() == true {
updateInfo := make(chan string)
go getLatestReleaseInfo(updateInfo)
util.CheckError(rootCmd.Execute(), "")
select {
case message := <-updateInfo:
fmt.Println(message)
default:
glog.V(4).Info("Could not get the latest release information in time. Never mind, exiting gracefully :)")
}
} else {
util.CheckError(rootCmd.Execute(), "")
}
}
func init() {
// Here you will define your flags and configuration settings.
// Cobra supports persistent flags, which, if defined here,
// will be global for your application.
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.odo.yaml)")
rootCmd.PersistentFlags().Bool(util.SkipConnectionCheckFlagName, false, "Skip cluster check")
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.CommandLine.Set("logtostderr", "true")
// Override the verbosity flag description
verbosity := pflag.Lookup("v")
verbosity.Usage += ". Level varies from 0 to 9 (default 0)."
rootCmd.SetUsageTemplate(rootUsageTemplate)
flag.CommandLine.Parse([]string{})
}
func getLatestReleaseInfo(info chan<- string) {
newTag, err := notify.CheckLatestReleaseTag(VERSION)
if err != nil {
// The error is intentionally not being handled because we don't want
// to stop the execution of the program because of this failure
glog.V(4).Infof("Error checking if newer odo release is available: %v", err)
}
if len(newTag) > 0 {
info <- "---\n" +
"A newer version of odo (version: " + fmt.Sprint(newTag) + ") is available.\n" +
"Update using your package manager, or run\n" +
"curl " + notify.InstallScriptURL + " | sh\n" +
"to update manually, or visit https://github.com/redhat-developer/odo/releases\n" +
"---\n" +
"If you wish to disable the update notifications, you can disable it by running\n" +
"'odo utils config set UpdateNotification false'\n"
}
}

View File

@@ -1,211 +0,0 @@
package cmd
import (
"fmt"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util"
"github.com/redhat-developer/odo/pkg/odo/util/completion"
"os"
"strings"
"text/tabwriter"
"github.com/golang/glog"
svc "github.com/redhat-developer/odo/pkg/service"
"github.com/spf13/cobra"
)
var (
serviceForceDeleteFlag bool
parameters []string
plan string
)
// serviceCmd represents the service command
var serviceCmd = &cobra.Command{
Use: "service",
Short: "Perform service catalog operations",
Long: ` Perform service catalog operations, Limited to template service broker only.`,
Example: fmt.Sprintf("%s\n%s\n%s",
serviceCreateCmd.Example,
serviceDeleteCmd.Example,
serviceListCmd.Example),
Args: cobra.RangeArgs(1, 3),
}
var serviceCreateCmd = &cobra.Command{
Use: "create <service_type> --plan <plan_name> [service_name]",
Short: "Create a new service",
Long: `Create a new service from service catalog using the plan defined and deploy it on OpenShift.
If service name is not provided, service type value will be used. The plan to be used must be passed along the service type
using this convention <service_type>/<plan>. The parameters to configure the service are passed as a list of key=value pairs.
The list of the parameters and their type is defined according to the plan selected.
A full list of service types that can be deployed are available using: 'odo catalog list services'`,
Example: ` # Create new postgresql service from service catalog using dev plan and name my-postgresql-db.
odo service create dh-postgresql-apb my-postgresql-db --plan dev -p postgresql_user=luke -p postgresql_password=secret
`,
Args: cobra.RangeArgs(1, 2),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContextCreatingAppIfNeeded(cmd)
client := context.Client
applicationName := context.Application
// make sure the service type exists
serviceType := args[0]
matchingService, err := svc.GetSvcByType(client, serviceType)
util.CheckError(err, "unable to create service because Service Catalog is not enabled in your cluster")
if matchingService == nil {
fmt.Printf("Service %v doesn't exist\nRun 'odo service catalog' to see a list of supported services.\n", serviceType)
os.Exit(1)
}
if len(plan) == 0 {
// when the plan has not been supplied, if there is only one available plan, we select it
if len(matchingService.PlanList) == 1 {
plan = matchingService.PlanList[0]
glog.V(4).Infof("Plan %s was automatically selected since it's the only one available for service %s", plan, serviceType)
} else {
fmt.Printf("No plan was supplied for service %v.\nPlease select one of: %v\n", serviceType, strings.Join(matchingService.PlanList, ","))
os.Exit(1)
}
} else {
// when the plan has been supplied, we need to make sure it exists
planFound := false
for _, candidatePlan := range matchingService.PlanList {
if plan == candidatePlan {
planFound = true
break
}
}
if !planFound {
fmt.Printf("Plan %s is invalid for service %v.\nPlease select one of: %v\n", plan, serviceType, strings.Join(matchingService.PlanList, ","))
os.Exit(1)
}
}
// if only one arg is given, then it is considered as service name and service type both
serviceName := serviceType
// if two args are given, first is service type and second one is service name
if len(args) == 2 {
serviceName = args[1]
}
//validate service name
err = validateName(serviceName)
util.CheckError(err, "")
exists, err := svc.SvcExists(client, serviceName, applicationName)
util.CheckError(err, "")
if exists {
fmt.Printf("%s service already exists in the current application.\n", serviceName)
os.Exit(1)
}
err = svc.CreateService(client, serviceName, serviceType, plan, parameters, applicationName)
util.CheckError(err, "")
fmt.Printf(`Service '%s' was created.
Progress of the provisioning will not be reported and might take a long time.
You can see the current status by executing 'odo service list'`, serviceName)
},
}
var serviceDeleteCmd = &cobra.Command{
Use: "delete <service_name>",
Short: "Delete an existing service",
Long: "Delete an existing service",
Example: ` # Delete the service named 'mysql-persistent'
odo service delete mysql-persistent
`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
glog.V(4).Infof("service delete called\n args: %#v", strings.Join(args, " "))
context := genericclioptions.NewContext(cmd)
client := context.Client
applicationName := context.Application
serviceName := args[0]
// Checks to see if the service actually exists
exists, err := svc.SvcExists(client, serviceName, applicationName)
util.CheckError(err, "unable to delete service because Service Catalog is not enabled in your cluster")
if !exists {
fmt.Printf("Service with the name %s does not exist in the current application\n", serviceName)
os.Exit(1)
}
var confirmDeletion string
if serviceForceDeleteFlag {
confirmDeletion = "y"
} else {
fmt.Printf("Are you sure you want to delete %v from %v? [y/N] ", serviceName, applicationName)
fmt.Scanln(&confirmDeletion)
}
if strings.ToLower(confirmDeletion) == "y" {
err := svc.DeleteService(client, serviceName, applicationName)
util.CheckError(err, "")
fmt.Printf("Service %s from application %s has been deleted\n", serviceName, applicationName)
} else {
fmt.Printf("Aborting deletion of service: %v\n", serviceName)
}
},
}
var serviceListCmd = &cobra.Command{
Use: "list",
Short: "List all services in the current application",
Long: "List all services in the current application",
Example: ` # List all services in the application
odo service list
`,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
applicationName := context.Application
services, err := svc.ListWithDetailedStatus(client, applicationName)
util.CheckError(err, "Service Catalog is not enabled in your cluster")
if len(services) == 0 {
fmt.Println("There are no services deployed for this application")
return
}
w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
fmt.Fprintln(w, "NAME", "\t", "TYPE", "\t", "STATUS")
for _, comp := range services {
fmt.Fprintln(w, comp.Name, "\t", comp.Type, "\t", comp.Status)
}
w.Flush()
},
}
func init() {
serviceDeleteCmd.Flags().BoolVarP(&serviceForceDeleteFlag, "force", "f", false, "Delete service without prompting")
serviceCreateCmd.Flags().StringVar(&plan, "plan", "", "The name of the plan of the service to be created")
serviceCreateCmd.Flags().StringSliceVarP(&parameters, "parameters", "p", []string{}, "Parameters of the plan where a parameter is expressed as <key>=<value")
// Add a defined annotation in order to appear in the help menu
serviceCmd.Annotations = map[string]string{"command": "other"}
serviceCmd.SetUsageTemplate(cmdUsageTemplate)
serviceCmd.AddCommand(serviceCreateCmd)
serviceCmd.AddCommand(serviceDeleteCmd)
serviceCmd.AddCommand(serviceListCmd)
//Adding `--project` flag
addProjectFlag(serviceCreateCmd)
addProjectFlag(serviceDeleteCmd)
addProjectFlag(serviceListCmd)
//Adding `--application` flag
addApplicationFlag(serviceCreateCmd)
addApplicationFlag(serviceDeleteCmd)
addApplicationFlag(serviceListCmd)
rootCmd.AddCommand(serviceCmd)
completion.RegisterCommandHandler(serviceCreateCmd, completion.ServiceClassCompletionHandler)
completion.RegisterCommandHandler(serviceDeleteCmd, completion.ServiceCompletionHandler)
}

View File

@@ -1,106 +0,0 @@
package cmd
import (
"github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1"
"github.com/posener/complete"
componentlabels "github.com/redhat-developer/odo/pkg/component/labels"
"github.com/redhat-developer/odo/pkg/occlient"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util/completion"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ktesting "k8s.io/client-go/testing"
"sort"
"testing"
)
func TestCompletions(t *testing.T) {
t.Parallel()
tests := []struct {
name string
handler completion.ContextualizedPredictor
cmd *cobra.Command
last string
want []string
}{
{
name: "Completing service create without input returns all available service class external names",
handler: completion.ServiceClassCompletionHandler,
cmd: serviceCreateCmd,
want: []string{"foo", "bar", "boo"},
},
{
name: "Completing service delete without input returns all available service instances",
handler: completion.ServiceCompletionHandler,
cmd: serviceDeleteCmd,
want: []string{"foo"},
},
}
client, fakeClientSet := occlient.FakeNew()
fakeClientSet.ServiceCatalogClientSet.PrependReactor("list", "clusterserviceclasses", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &v1beta1.ClusterServiceClassList{
Items: []v1beta1.ClusterServiceClass{
fakeClusterServiceClass("foo"),
fakeClusterServiceClass("bar"),
fakeClusterServiceClass("boo"),
},
}, nil
})
fakeClientSet.ServiceCatalogClientSet.PrependReactor("list", "serviceinstances", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &v1beta1.ServiceInstanceList{
Items: []v1beta1.ServiceInstance{
{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"app.kubernetes.io/name": "foo", componentlabels.ComponentLabel: "foo", componentlabels.ComponentTypeLabel: "service"},
},
Status: v1beta1.ServiceInstanceStatus{
Conditions: []v1beta1.ServiceInstanceCondition{
{
Reason: "some reason",
},
},
},
},
},
}, nil
})
context := genericclioptions.NewFakeContext("", "", "", client)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := complete.Args{Last: tt.last}
got := tt.handler(tt.cmd, completion.NewParsedArgs(a, tt.cmd), context)
if !equal(got, tt.want) {
t.Errorf("Failed %s: got: %q, want: %q", t.Name(), got, tt.want)
}
})
}
}
func fakeClusterServiceClass(name string) v1beta1.ClusterServiceClass {
return v1beta1.ClusterServiceClass{
Spec: v1beta1.ClusterServiceClassSpec{
CommonServiceClassSpec: v1beta1.CommonServiceClassSpec{
ExternalName: name,
},
},
}
}
func equal(s1, s2 []string) bool {
sort.Strings(s1)
sort.Strings(s2)
if len(s1) != len(s2) {
return false
}
for i := range s1 {
if s1[i] != s2[i] {
return false
}
}
return true
}

View File

@@ -1,288 +0,0 @@
package cmd
import (
"fmt"
"os"
"strings"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
"github.com/redhat-developer/odo/pkg/odo/util/completion"
"github.com/redhat-developer/odo/pkg/storage"
"github.com/redhat-developer/odo/pkg/util"
"github.com/spf13/cobra"
)
var (
storageSize string
storagePath string
storageForceDeleteflag bool
storageAllListflag bool
)
var storageCmd = &cobra.Command{
Use: "storage",
Short: "Perform storage operations",
Long: "Perform storage operations",
Example: fmt.Sprintf("%s\n%s\n%s\n%s",
storageCreateCmd.Example,
storageDeleteCmd.Example,
storageUnmountCmd.Example,
storageListCmd.Example),
}
var storageCreateCmd = &cobra.Command{
Use: "create",
Short: "Create storage and mount to a component",
Long: "Create storage and mount to a component",
Example: ` # Create storage of size 1Gb to a component
odo storage create mystorage --path=/opt/app-root/src/storage/ --size=1Gi
`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
applicationName := context.Application
componentName := context.Component()
var storageName string
if len(args) != 0 {
storageName = args[0]
} else {
storageName = componentName + "-" + util.GenerateRandomString(4)
}
// validate storage path
err := validateStoragePath(client, storagePath, componentName, applicationName)
odoutil.CheckError(err, "")
_, err = storage.Create(client, storageName, storageSize, storagePath, componentName, applicationName)
odoutil.CheckError(err, "")
fmt.Printf("Added storage %v to %v\n", storageName, componentName)
},
}
var storageUnmountCmd = &cobra.Command{
Use: "unmount PATH | STORAGE_NAME",
Short: "Unmount storage from the given path or identified by its name, from the current component",
Long: `Unmount storage from the given path or identified by its name, from the current component.
The storage and the contents are not deleted, the storage or storage with the given path, is only unmounted from the component, and hence is no longer accessible by the component.`,
Example: ` # Unmount storage 'dbstorage' from current component
odo storage unmount dbstorage
# Unmount storage 'database' from component 'mongodb'
odo storage unmount database --component mongodb
# Unmount storage mounted to path '/data' from current component
odo storage unmount /data
# Unmount storage mounted to path '/data' from component 'mongodb'
odo storage unmount /data --component mongodb
`,
Aliases: []string{"umount"},
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
applicationName := context.Application
componentName := context.Component()
var storageName string
var err error
// checking if the first character in the argument is a "/", indicating a path or not, indicating a storage name
if string(args[0][0]) == "/" {
path := args[0]
storageName, err = storage.GetStorageNameFromMountPath(client, path, componentName, applicationName)
odoutil.CheckError(err, "Unable to get storage name from mount path")
if storageName == "" {
fmt.Printf("No storage is mounted to %s in the component %s\n", path, componentName)
os.Exit(1)
}
} else {
storageName = args[0]
exists, err := storage.IsMounted(client, storageName, componentName, applicationName)
odoutil.CheckError(err, "Unable to check if storage is mounted or not")
if !exists {
fmt.Printf("Storage %v does not exist in component %v\n", storageName, componentName)
os.Exit(1)
}
}
err = storage.Unmount(client, storageName, componentName, applicationName, true)
odoutil.CheckError(err, "Unable to unmount storage %v from component %v", storageName, componentName)
fmt.Printf("Unmounted storage %v from %v\n", storageName, componentName)
},
}
var storageDeleteCmd = &cobra.Command{
Use: "delete",
Short: "Delete storage from component",
Example: ` # Delete storage mystorage from the currently active component
odo storage delete mystorage
# Delete storage mystorage from component 'mongodb'
odo storage delete mystorage --component mongodb
`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
applicationName := context.Application
storageName := args[0]
exists, err := storage.Exists(client, storageName, applicationName)
odoutil.CheckError(err, "")
if !exists {
fmt.Printf("The storage %v does not exists in the application %v\n", storageName, applicationName)
os.Exit(1)
}
componentName, err := storage.GetComponentNameFromStorageName(client, storageName)
if err != nil {
odoutil.CheckError(err, "Unable to get component associated with %s storage.", storageName)
}
var confirmDeletion string
if storageForceDeleteflag {
confirmDeletion = "y"
} else {
if componentName != "" {
mPath := storage.GetMountPath(client, storageName, componentName, applicationName)
fmt.Printf("Are you sure you want to delete the storage %v mounted to %v in %v component? [y/N] ", storageName, mPath, componentName)
} else {
fmt.Printf("Are you sure you want to delete the storage %v that is not currently mounted to any component? [y/N] ", storageName)
}
fmt.Scanln(&confirmDeletion)
}
if strings.ToLower(confirmDeletion) == "y" {
componentName, err = storage.Delete(client, storageName, applicationName)
odoutil.CheckError(err, "failed to delete storage")
if componentName != "" {
fmt.Printf("Deleted storage %v from %v\n", storageName, componentName)
} else {
fmt.Printf("Deleted storage %v\n", storageName)
}
} else {
fmt.Printf("Aborting deletion of storage: %v\n", storageName)
}
},
}
var storageListCmd = &cobra.Command{
Use: "list",
Short: "List storage attached to a component",
Long: "List storage attached to a component",
Example: ` # List all storage attached or mounted to the current component and
# all unattached or unmounted storage in the current application
odo storage list
`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
applicationName := context.Application
if storageAllListflag {
if len(genericclioptions.FlagValueIfSet(cmd, odoutil.ComponentFlagName)) > 0 {
fmt.Println("Invalid arguments. Component name is not needed")
os.Exit(1)
}
printMountedStorageInAllComponent(client, applicationName)
} else {
// storageComponent is the input component name
componentName := context.Component()
printMountedStorageInComponent(client, componentName, applicationName)
}
printUnmountedStorage(client, applicationName)
},
}
var storageMountCmd = &cobra.Command{
Use: "mount [storage name]",
Short: "mount storage to a component",
Example: ` # Mount storage 'dbstorage' to current component
odo storage mount dbstorage --path /data
# Mount storage 'database' to component 'mongodb'
odo storage mount database --component mongodb --path /data`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
applicationName := context.Application
componentName := context.Component()
storageName := args[0]
exists, err := storage.Exists(client, storageName, applicationName)
odoutil.CheckError(err, "unable to check if the storage exists in the current application")
if !exists {
fmt.Printf("The storage %v does not exists in the current application '%v'", storageName, applicationName)
os.Exit(1)
}
isMounted, err := storage.IsMounted(client, storageName, componentName, applicationName)
odoutil.CheckError(err, "unable to check if the component is already mounted or not")
if isMounted {
fmt.Printf("The storage %v is already mounted on the current component '%v'\n", storageName, componentName)
os.Exit(1)
}
err = storage.Mount(client, storagePath, storageName, componentName, applicationName)
odoutil.CheckError(err, "")
fmt.Printf("The storage %v is successfully mounted to the current component '%v'\n", storageName, componentName)
},
}
func init() {
storageCreateCmd.Flags().StringVar(&storageSize, "size", "", "Size of storage to add")
storageCreateCmd.Flags().StringVar(&storagePath, "path", "", "Path to mount the storage on")
storageCreateCmd.MarkFlagRequired("path")
storageCreateCmd.MarkFlagRequired("size")
storageDeleteCmd.Flags().BoolVarP(&storageForceDeleteflag, "force", "f", false, "Delete storage without prompting")
storageListCmd.Flags().BoolVarP(&storageAllListflag, "all", "a", false, "List all storage in all components")
storageMountCmd.Flags().StringVar(&storagePath, "path", "", "Path to mount the storage on")
storageMountCmd.MarkFlagRequired("path")
storageCmd.AddCommand(storageCreateCmd)
storageCmd.AddCommand(storageDeleteCmd)
storageCmd.AddCommand(storageUnmountCmd)
storageCmd.AddCommand(storageListCmd)
storageCmd.AddCommand(storageMountCmd)
//Adding `--project` flag
addProjectFlag(storageCreateCmd)
addProjectFlag(storageDeleteCmd)
addProjectFlag(storageListCmd)
addProjectFlag(storageMountCmd)
addProjectFlag(storageUnmountCmd)
//Adding `--application` flag
addApplicationFlag(storageCreateCmd)
addApplicationFlag(storageDeleteCmd)
addApplicationFlag(storageListCmd)
addApplicationFlag(storageMountCmd)
addApplicationFlag(storageUnmountCmd)
//Adding `--component` flag
addComponentFlag(storageCreateCmd)
addComponentFlag(storageDeleteCmd)
addComponentFlag(storageListCmd)
addComponentFlag(storageMountCmd)
addComponentFlag(storageUnmountCmd)
// Add a defined annotation in order to appear in the help menu
storageCmd.Annotations = map[string]string{"command": "other"}
storageCmd.SetUsageTemplate(cmdUsageTemplate)
rootCmd.AddCommand(storageCmd)
completion.RegisterCommandHandler(storageDeleteCmd, completion.StorageDeleteCompletionHandler)
completion.RegisterCommandHandler(storageMountCmd, completion.StorageMountCompletionHandler)
completion.RegisterCommandHandler(storageUnmountCmd, completion.StorageUnMountCompletionHandler)
}

View File

@@ -1,89 +0,0 @@
package cmd
import (
"fmt"
"github.com/redhat-developer/odo/pkg/odo/util"
"io"
"os"
"github.com/spf13/cobra"
)
// terminalCmd represents the terminal command
var terminalCmd = &cobra.Command{
Use: "terminal",
Short: "Add Odo terminal support to your development environment",
Long: `Add Odo terminal support to your development environment.
This will append your PS1 environment variable with Odo component and application information.`,
Example: ` # Bash terminal PS1 support
source <(odo utils terminal bash)
# Zsh terminal PS1 support
source <(odo utils terminal zsh)
`,
RunE: func(cmd *cobra.Command, args []string) error {
err := TerminalGenerate(os.Stdout, cmd, args)
util.CheckError(err, "")
return nil
},
}
// Generates the PS1 output for Odo terminal support (appends to current PS1 environment variable)
func TerminalGenerate(out io.Writer, cmd *cobra.Command, args []string) error {
// Check the passed in arguments
if len(args) == 0 {
return fmt.Errorf("Shell not specified. ex. odo completion [bash|zsh]")
}
if len(args) > 1 {
return fmt.Errorf("Too many arguments. Expected only the shell type. ex. odo completion [bash|zsh]")
}
shell := args[0]
// sh function for retrieving component information
var PS1 = `
__odo_ps1() {
# Get application context
APP=$(odo application get -q --skip-connection-check)
if [ "$APP" = "" ]; then
APP="<no application>"
fi
# Get current context
COMPONENT=$(odo component get -q --skip-connection-check)
if [ "$COMPONENT" = "" ]; then
COMPONENT="<no component>"
fi
if [ -n "$COMPONENT" ] || [ -n "$APP" ]; then
echo "[${APP}/${COMPONENT}]"
fi
}
`
// Bash output
var bashPS1Output = PS1 + `
PS1='$(__odo_ps1)'$PS1
`
// Zsh output
var zshPS1Output = PS1 + `
setopt prompt_subst
PROMPT='$(__odo_ps1)'$PROMPT
`
if shell == "bash" {
out.Write([]byte(bashPS1Output))
} else if shell == "zsh" {
out.Write([]byte(zshPS1Output))
} else {
return fmt.Errorf("not a compatible shell, bash and zsh are only supported")
}
return nil
}

View File

@@ -1,118 +0,0 @@
package cmd
import (
"fmt"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util"
"github.com/redhat-developer/odo/pkg/odo/util/completion"
"os"
"path/filepath"
"github.com/fatih/color"
"github.com/redhat-developer/odo/pkg/component"
"github.com/spf13/cobra"
)
var updateCmd = &cobra.Command{
Use: "update",
Args: cobra.MaximumNArgs(1),
Short: "Update the source code path of a component",
Long: "Update the source code path of a component",
Example: ` # Change the source code path of a currently active component to local (use the current directory as a source)
odo update --local
# Change the source code path of the frontend component to local with source in ./frontend directory
odo update frontend --local ./frontend
# Change the source code path of a currently active component to git
odo update --git https://github.com/openshift/nodejs-ex.git
# Change the source code path of the component named node-ex to git
odo update node-ex --git https://github.com/openshift/nodejs-ex.git
# Change the source code path of the component named wildfly to a binary named sample.war in ./downloads directory
odo update wildfly --binary ./downloads/sample.war
`,
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
applicationName := context.Application
stdout := color.Output
checkFlag := 0
if len(componentBinary) != 0 {
checkFlag++
}
if len(componentGit) != 0 {
checkFlag++
}
if len(componentLocal) != 0 {
checkFlag++
}
if checkFlag != 1 {
fmt.Println("The source can be either --binary or --local or --git")
os.Exit(1)
}
var componentName string
if len(args) == 0 {
componentName = context.Component()
} else {
componentName = context.Component(args[0])
}
if len(applicationName) == 0 {
fmt.Println("Cannot update as no application is set as active")
os.Exit(1)
}
if len(componentGit) != 0 {
err := component.Update(client, componentName, applicationName, "git", componentGit, stdout)
util.CheckError(err, "")
fmt.Printf("The component %s was updated successfully\n", componentName)
} else if len(componentLocal) != 0 {
// we want to use and save absolute path for component
dir, err := filepath.Abs(componentLocal)
util.CheckError(err, "")
fileInfo, err := os.Stat(dir)
util.CheckError(err, "")
if !fileInfo.IsDir() {
fmt.Println("Please provide a path to the directory")
os.Exit(1)
}
err = component.Update(client, componentName, applicationName, "local", dir, stdout)
util.CheckError(err, "")
fmt.Printf("The component %s was updated successfully, please use 'odo push' to push your local changes\n", componentName)
} else if len(componentBinary) != 0 {
path, err := filepath.Abs(componentBinary)
util.CheckError(err, "")
err = component.Update(client, componentName, applicationName, "binary", path, stdout)
util.CheckError(err, "")
fmt.Printf("The component %s was updated successfully, please use 'odo push' to push your local changes\n", componentName)
}
},
}
func init() {
updateCmd.Flags().StringVarP(&componentBinary, "binary", "b", "", "binary artifact")
updateCmd.Flags().StringVarP(&componentGit, "git", "g", "", "git source")
updateCmd.Flags().StringVarP(&componentLocal, "local", "l", "", "Use local directory as a source for component.")
// Add a defined annotation in order to appear in the help menu
updateCmd.Annotations = map[string]string{"command": "component"}
updateCmd.SetUsageTemplate(cmdUsageTemplate)
//Adding `--application` flag
addApplicationFlag(updateCmd)
//Adding `--project` flag
addProjectFlag(updateCmd)
completion.RegisterCommandFlagHandler(updateCmd, "local", completion.FileCompletionHandler)
completion.RegisterCommandFlagHandler(updateCmd, "binary", completion.FileCompletionHandler)
rootCmd.AddCommand(updateCmd)
}

View File

@@ -1,206 +0,0 @@
package cmd
import (
"fmt"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util/completion"
"os"
"strings"
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
"github.com/redhat-developer/odo/pkg/util"
"text/tabwriter"
"github.com/redhat-developer/odo/pkg/url"
"github.com/spf13/cobra"
)
var (
urlForceDeleteFlag bool
urlOpenFlag bool
urlPort int
)
var urlCmd = &cobra.Command{
Use: "url",
Short: "Expose component to the outside world",
Long: `Expose component to the outside world.
The URLs that are generated using this command, can be used to access the deployed components from outside the cluster.`,
Example: fmt.Sprintf("%s\n%s\n%s",
urlCreateCmd.Example,
urlDeleteCmd.Example,
urlListCmd.Example),
}
var urlCreateCmd = &cobra.Command{
Use: "create [component name]",
Short: "Create a URL for a component",
Long: `Create a URL for a component.
The created URL can be used to access the specified component from outside the OpenShift cluster.
`,
Example: ` # Create a URL for the current component with a specific port
odo url create --port 8080
# Create a URL with a specific name and port
odo url create example --port 8080
# Create a URL with a specific name by automatic detection of port (only for components which expose only one service port)
odo url create example
# Create a URL with a specific name and port for component frontend
odo url create example --port 8080 --component frontend
`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
applicationName := context.Application
componentName := context.Component()
var urlName string
switch len(args) {
case 0:
urlName = componentName
case 1:
urlName = args[0]
default:
fmt.Println("unable to get component")
os.Exit(1)
}
exists, err := url.Exists(client, urlName, "", applicationName)
if exists {
fmt.Printf("The url %s already exists in the application: %s\n", urlName, applicationName)
os.Exit(1)
}
fmt.Printf("Adding URL to component: %v\n", componentName)
urlRoute, err := url.Create(client, urlName, urlPort, componentName, applicationName)
odoutil.CheckError(err, "")
urlCreated := url.GetUrlString(*urlRoute)
fmt.Printf("URL created for component: %v\n\n"+
"%v - %v\n", componentName, urlRoute.Name, urlCreated)
if urlOpenFlag {
err := util.OpenBrowser(urlCreated)
odoutil.CheckError(err, "Unable to open URL within default browser")
}
},
}
var urlDeleteCmd = &cobra.Command{
Use: "delete <url-name>",
Short: "Delete a URL",
Long: `Delete the given URL, hence making the service inaccessible.`,
Example: ` # Delete a URL to a component
odo url delete myurl
`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
applicationName := context.Application
componentName := context.Component()
urlName := args[0]
exists, err := url.Exists(client, urlName, componentName, applicationName)
odoutil.CheckError(err, "")
if !exists {
fmt.Printf("The URL %s does not exist within the component %s\n", urlName, componentName)
os.Exit(1)
}
var confirmDeletion string
if urlForceDeleteFlag {
confirmDeletion = "y"
} else {
fmt.Printf("Are you sure you want to delete the url %v? [y/N] ", urlName)
fmt.Scanln(&confirmDeletion)
}
if strings.ToLower(confirmDeletion) == "y" {
err = url.Delete(client, urlName, applicationName)
odoutil.CheckError(err, "")
fmt.Printf("Deleted URL: %v\n", urlName)
} else {
fmt.Printf("Aborting deletion of url: %v\n", urlName)
}
},
}
var urlListCmd = &cobra.Command{
Use: "list",
Short: "List URLs",
Long: `Lists all the available URLs which can be used to access the components.`,
Example: ` # List the available URLs
odo url list
`,
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
context := genericclioptions.NewContext(cmd)
client := context.Client
applicationName := context.Application
componentName := context.Component()
urls, err := url.List(client, componentName, applicationName)
odoutil.CheckError(err, "")
if len(urls) == 0 {
fmt.Printf("No URLs found for component %v in application %v\n", componentName, applicationName)
} else {
fmt.Printf("Found the following URLs for component %v in application %v:\n", componentName, applicationName)
tabWriterURL := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
//create headers
fmt.Fprintln(tabWriterURL, "NAME", "\t", "URL", "\t", "PORT")
for _, u := range urls {
fmt.Fprintln(tabWriterURL, u.Name, "\t", url.GetUrlString(u), "\t", u.Port)
}
tabWriterURL.Flush()
}
},
}
func init() {
urlCreateCmd.Flags().IntVarP(&urlPort, "port", "", -1, "port number for the url of the component, required in case of components which expose more than one service port")
urlCreateCmd.Flags().BoolVar(&urlOpenFlag, "open", false, "open the created link with your default browser")
urlDeleteCmd.Flags().BoolVarP(&urlForceDeleteFlag, "force", "f", false, "Delete url without prompting")
urlCmd.AddCommand(urlListCmd)
urlCmd.AddCommand(urlDeleteCmd)
urlCmd.AddCommand(urlCreateCmd)
// Add a defined annotation in order to appear in the help menu
urlCmd.Annotations = map[string]string{"command": "other"}
urlCmd.SetUsageTemplate(cmdUsageTemplate)
//Adding `--project` flag
addProjectFlag(urlListCmd)
addProjectFlag(urlCreateCmd)
addProjectFlag(urlDeleteCmd)
//Adding `--application` flag
addApplicationFlag(urlListCmd)
addApplicationFlag(urlDeleteCmd)
addApplicationFlag(urlCreateCmd)
//Adding `--component` flag
addComponentFlag(urlDeleteCmd)
addComponentFlag(urlListCmd)
addComponentFlag(urlCreateCmd)
rootCmd.AddCommand(urlCmd)
completion.RegisterCommandHandler(urlDeleteCmd, completion.URLCompletionHandler)
}

View File

@@ -1,31 +0,0 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
// utilsCmd represents the utils command
var utilsCmd = &cobra.Command{
Use: "utils",
Short: "Utilities for terminal commands and modifying Odo configurations",
Long: `Utilities for terminal commands and modifying Odo configurations`,
Example: fmt.Sprintf("%s\n%s\n%s",
terminalCmd.Example,
configurationSetCmd.Example,
configurationViewCmd.Example),
}
func init() {
utilsCmd.Annotations = map[string]string{"command": "utility"}
utilsCmd.SetUsageTemplate(cmdUsageTemplate)
configurationCmd.AddCommand(configurationViewCmd)
configurationCmd.AddCommand(configurationSetCmd)
configurationCmd.SetUsageTemplate(cmdUsageTemplate)
utilsCmd.AddCommand(configurationCmd)
utilsCmd.AddCommand(terminalCmd)
rootCmd.AddCommand(utilsCmd)
}

View File

@@ -1,73 +0,0 @@
package cmd
import (
"fmt"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"github.com/redhat-developer/odo/pkg/odo/util"
"os"
"strings"
"github.com/golang/glog"
"github.com/spf13/cobra"
)
var (
// VERSION is version number that will be displayed when running ./odo version
VERSION = "v0.0.16"
// GITCOMMIT is hash of the commit that wil be displayed when running ./odo version
// this will be overwritten when running build like this: go build -ldflags="-X github.com/redhat-developer/odo/cmd.GITCOMMIT=$(GITCOMMIT)"
// HEAD is default indicating that this was not set during build
GITCOMMIT = "HEAD"
)
var clientFlag bool
// versionCmd represents the version command
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the client version information",
Long: "Print the client version information",
Example: ` # Print the client version of Odo
odo version
`,
Run: func(cmd *cobra.Command, args []string) {
// If verbose mode is enabled, dump all KUBECLT_* env variables
// this is usefull for debuging oc plugin integration
for _, v := range os.Environ() {
if strings.HasPrefix(v, "KUBECTL_") {
glog.V(4).Info(v)
}
}
fmt.Println("odo " + VERSION + " (" + GITCOMMIT + ")")
if !clientFlag {
// Lets fetch the info about the server
serverInfo, err := genericclioptions.ClientWithConnectionCheck(cmd, true).GetServerVersion()
util.CheckError(err, "")
// make sure we only include Openshift info if we actually have it
openshiftStr := ""
if len(serverInfo.OpenShiftVersion) > 0 {
openshiftStr = fmt.Sprintf("OpenShift: %v\n", serverInfo.OpenShiftVersion)
}
fmt.Printf("\n"+
"Server: %v\n"+
"%v"+
"Kubernetes: %v\n",
serverInfo.Address,
openshiftStr,
serverInfo.KubernetesVersion)
}
},
}
func init() {
// Add a defined annotation in order to appear in the help menu
versionCmd.Annotations = map[string]string{"command": "utility"}
versionCmd.SetUsageTemplate(cmdUsageTemplate)
versionCmd.Flags().BoolVar(&clientFlag, "client", false, "Client version only (no server required).")
rootCmd.AddCommand(versionCmd)
}

View File

@@ -1,96 +0,0 @@
package cmd
import (
"fmt"
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
"net/url"
"os"
"runtime"
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
"github.com/redhat-developer/odo/pkg/component"
"github.com/redhat-developer/odo/pkg/util"
"github.com/golang/glog"
"github.com/spf13/cobra"
)
var (
ignores []string
delay int
)
var watchCmd = &cobra.Command{
Use: "watch [component name]",
Short: "Watch for changes, update component on change",
Long: `Watch for changes, update component on change.`,
Example: ` # Watch for changes in directory for current component
odo watch
# Watch for changes in directory for component called frontend
odo watch frontend
`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
stdout := os.Stdout
context := genericclioptions.NewContext(cmd)
client := context.Client
projectName := context.Project
applicationName := context.Application
// TODO: check if we can use context.Component() here
var componentName string
if len(args) == 0 {
var err error
glog.V(4).Info("No component name passed, assuming current component")
componentName, err = component.GetCurrent(applicationName, projectName)
odoutil.CheckError(err, "")
if componentName == "" {
fmt.Println("No component is set as active.")
fmt.Println("Use 'odo component set <component name> to set and existing component as active or call this command with component name as and argument.")
os.Exit(1)
}
} else {
componentName = args[0]
}
sourceType, sourcePath, err := component.GetComponentSource(client, componentName, applicationName)
odoutil.CheckError(err, "Unable to get source for %s component.", componentName)
if sourceType != "binary" && sourceType != "local" {
fmt.Printf("Watch is supported by binary and local components only and source type of component %s is %s\n", componentName, sourceType)
os.Exit(1)
}
u, err := url.Parse(sourcePath)
odoutil.CheckError(err, "Unable to parse source %s from component %s.", sourcePath, componentName)
if u.Scheme != "" && u.Scheme != "file" {
fmt.Printf("Component %s has invalid source path %s.", componentName, u.Scheme)
os.Exit(1)
}
watchPath := util.ReadFilePath(u, runtime.GOOS)
err = component.WatchAndPush(client, componentName, applicationName, watchPath, stdout, ignores, delay, make(chan string), component.PushLocal)
odoutil.CheckError(err, "Error while trying to watch %s", watchPath)
},
}
func init() {
// ignore git as it can change even if no source file changed
// for example some plugins providing git info in PS1 doing that
watchCmd.Flags().StringSliceVar(&ignores, "ignore", []string{".*\\.git.*"}, "Files or folders to be ignored via regular expressions.")
watchCmd.Flags().IntVar(&delay, "delay", 1, "Time in seconds between a detection of code change and push.delay=0 means changes will be pushed as soon as they are detected which can cause performance issues")
// Add a defined annotation in order to appear in the help menu
watchCmd.Annotations = map[string]string{"command": "component"}
watchCmd.SetUsageTemplate(cmdUsageTemplate)
//Adding `--application` flag
addApplicationFlag(watchCmd)
//Adding `--project` flag
addProjectFlag(watchCmd)
rootCmd.AddCommand(watchCmd)
}