Skip to content

Commit

Permalink
Issue minishift#126 Ability to create multiple instances of Minishift
Browse files Browse the repository at this point in the history
Introducing --profile to minishift start and profile command

Signed-off-by: Lalatendu Mohanty <lmohanty@redhat.com>
  • Loading branch information
LalatenduMohanty committed Sep 13, 2017
1 parent 5cfcaf0 commit cdf8ac6
Show file tree
Hide file tree
Showing 24 changed files with 904 additions and 94 deletions.
3 changes: 2 additions & 1 deletion cmd/minishift/cmd/delete.go
Expand Up @@ -22,6 +22,7 @@ import (

"github.com/docker/machine/libmachine"
"github.com/minishift/minishift/cmd/minishift/cmd/util"
cmdUtil "github.com/minishift/minishift/cmd/minishift/cmd/util"
"github.com/minishift/minishift/pkg/minikube/cluster"
"github.com/minishift/minishift/pkg/minikube/constants"
minishiftConfig "github.com/minishift/minishift/pkg/minishift/config"
Expand Down Expand Up @@ -50,7 +51,7 @@ func runDelete(cmd *cobra.Command, args []string) {
util.ExitIfUndefined(api, constants.MachineName)

// Unregistration, do not allow to be skipped
unregisterHost(api, false)
cmdUtil.UnregisterHost(api, false)

if clearCache {
cachePath := constants.MakeMiniPath("cache")
Expand Down
35 changes: 31 additions & 4 deletions cmd/minishift/cmd/oc_env.go
Expand Up @@ -18,17 +18,24 @@ package cmd

import (
"fmt"

"github.com/minishift/minishift/pkg/minikube/constants"

"os"
"path/filepath"
"text/template"

"github.com/docker/machine/libmachine"
configCmd "github.com/minishift/minishift/cmd/minishift/cmd/config"
"github.com/minishift/minishift/cmd/minishift/cmd/util"
"github.com/minishift/minishift/pkg/minishift/cache"
"github.com/minishift/minishift/pkg/minishift/clusterup"
"github.com/minishift/minishift/pkg/minishift/config"
profileActions "github.com/minishift/minishift/pkg/minishift/profile"
"github.com/minishift/minishift/pkg/util/os/atexit"
"github.com/minishift/minishift/pkg/util/shell"
"github.com/spf13/cobra"
"os"
"path/filepath"
"text/template"
"github.com/spf13/viper"
)

const (
Expand Down Expand Up @@ -83,7 +90,14 @@ var ocEnvCmd = &cobra.Command{

var shellCfg *OcShellConfig

shellCfg, err = getOcShellConfig(config.InstanceConfig.OcPath, forceShell)
// When oc-env --profile PROFILE_NAME is used and PROFILE_NAME is not an active profile
var ocPath string
if constants.ProfileName != profileActions.GetActiveProfile() {
ocPath = getOcPath()
} else {
ocPath = config.InstanceConfig.OcPath
}
shellCfg, err = getOcShellConfig(ocPath, forceShell)
if err != nil {
atexit.ExitWithMessage(1, fmt.Sprintf("Error running the oc-env command: %s", err.Error()))
}
Expand All @@ -96,3 +110,16 @@ func init() {
RootCmd.AddCommand(ocEnvCmd)
ocEnvCmd.Flags().StringVar(&forceShell, "shell", "", "Force setting the environment for a specified shell: [fish, cmd, powershell, tcsh, bash, zsh]. Default is auto-detect.")
}

// Get the oc path as per the current profile.
// Because InstanceConfig.OcPath is set in minishift start or profile set. So when oc-env is called with --profile
// it points to last minishift start or profile set.
func getOcPath() string {
requestedOpenShiftVersion := viper.GetString(configCmd.OpenshiftVersion.Name)
ocVersion := clusterup.DetermineOcVersion(requestedOpenShiftVersion)
ocBinary := cache.Oc{
OpenShiftVersion: ocVersion,
MinishiftCacheDir: filepath.Join(constants.Minipath, "cache"),
}
return filepath.Join(ocBinary.GetCacheFilepath(), constants.OC_BINARY_NAME)
}
36 changes: 36 additions & 0 deletions cmd/minishift/cmd/profile/profile.go
@@ -0,0 +1,36 @@
/*
Copyright (C) 2017 Red Hat, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package profile

import (
"github.com/spf13/cobra"
)

const (
emptyProfileMessage = "You must provide the profile name. Run `minishift profile list` to view profiles"
extraArgumentMessage = "You have provided more arguments than required. You must provide a single profile name"
)

var ProfileCmd = &cobra.Command{
Use: "profile SUBCOMMAND [flags]",
Aliases: []string{"instance", "profiles"},
Short: "Manages Minishift profiles.",
Long: "Allows you to create and manage multiple Minishift instances. Use the sub-commands to list, activate or delete existing profiles.",
Run: func(cmd *cobra.Command, args []string) {
cmd.Help()
},
}
114 changes: 114 additions & 0 deletions cmd/minishift/cmd/profile/profile_delete.go
@@ -0,0 +1,114 @@
/*
Copyright (C) 2017 Red Hat, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package profile

import (
"fmt"
"os"

"github.com/docker/machine/libmachine"
cmdUtil "github.com/minishift/minishift/cmd/minishift/cmd/util"
"github.com/minishift/minishift/pkg/minikube/cluster"
"github.com/minishift/minishift/pkg/minikube/constants"
profileActions "github.com/minishift/minishift/pkg/minishift/profile"
pkgUtil "github.com/minishift/minishift/pkg/util"
"github.com/minishift/minishift/pkg/util/os/atexit"
"github.com/spf13/cobra"
)

var (
profileDeleteCmd = &cobra.Command{
Use: "delete PROFILE_NAME",
Short: "delete profiles.",
Long: "deletes an existing profile.",
Run: runProfileDelete,
}
force bool
)

func runProfileDelete(cmd *cobra.Command, args []string) {
if len(args) == 0 {
atexit.ExitWithMessage(1, emptyProfileMessage)
} else if len(args) > 1 {
atexit.ExitWithMessage(1, extraArgumentMessage)
}

profileName := args[0]
var defaultProfile bool

if !cmdUtil.IsValidProfile(profileName) {
atexit.ExitWithMessage(1, fmt.Sprintf("Error: '%s' is not a valid profile", profileName))
}

profileActions.UpdateProfileConstants(profileName)
profileBaseDir := constants.Minipath

if profileName == constants.DefaultProfileName {
defaultProfile = true
}
if defaultProfile {
atexit.ExitWithMessage(1, fmt.Sprintf("Default profile '%s' can not be deleted", profileName))
}

if !force {
var hasConfirmed bool
if profileActions.GetActiveProfile() == profileName {
hasConfirmed = pkgUtil.AskForConfirmation("You are deleting the active profile. It will remove the VM and all related artifacts.")
} else {
hasConfirmed = pkgUtil.AskForConfirmation("Will remove the VM and all the related artifacts.")
}
if !hasConfirmed {
atexit.Exit(1)
}
}

api := libmachine.NewClient(profileBaseDir, constants.MakeMiniPath("certs"))
defer api.Close()

exists := cmdUtil.VMExists(api, constants.MachineName)
if !exists {
fmt.Println(fmt.Sprintf("VM for profile '%s' does not exist", profileName))
} else {
cmdUtil.UnregisterHost(api, false)
err := cluster.DeleteHost(api)
if err != nil {
fmt.Println(fmt.Sprintf("Error deleting '%s': %v", constants.MakeMiniPath("machines"), err.Error()))
}
}

err := os.RemoveAll(profileBaseDir)
if err != nil {
atexit.ExitWithMessage(1, fmt.Sprintf("Error deleting '%s': %v", profileBaseDir, err.Error()))
} else {
fmt.Println("Deleted: ", profileBaseDir)
}
fmt.Println(fmt.Sprintf("Profile '%s' deleted successfully", profileName))

// When active profile is deleted, reset the active profile to default profile
if profileActions.GetActiveProfile() == profileName {
fmt.Println(fmt.Sprintf("Switching to default profile '%s' as the active profile.", constants.DefaultProfileName))
err = profileActions.SetActiveProfile(constants.DefaultProfileName)
if err != nil {
atexit.ExitWithMessage(1, fmt.Sprintf("Error setting default profile '%s' as the active profile.", constants.DefaultProfileName))
}
}
}

func init() {
profileDeleteCmd.Flags().BoolVar(&force, "force", false, "Forces the deletion of profile and related files in MINISHIFT_HOME.")
ProfileCmd.AddCommand(profileDeleteCmd)
}
57 changes: 57 additions & 0 deletions cmd/minishift/cmd/profile/profile_list.go
@@ -0,0 +1,57 @@
/*
Copyright (C) 2017 Red Hat, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package profile

import (
"fmt"
"os"
"text/tabwriter"

cmdUtil "github.com/minishift/minishift/cmd/minishift/cmd/util"
profileActions "github.com/minishift/minishift/pkg/minishift/profile"
"github.com/spf13/cobra"
)

var profileListCmd = &cobra.Command{
Use: "list",
Short: "Lists profiles.",
Long: "Lists the existing profiles.",
Run: func(cmd *cobra.Command, args []string) {
profiles := profileActions.GetProfileList()
displayProfiles(profiles)
},
}

func displayProfiles(profiles []string) {
display := new(tabwriter.Writer)
display.Init(os.Stdout, 0, 8, 0, '\t', 0)

activeProfile := profileActions.GetActiveProfile()
for _, profile := range profiles {
vmStatus := cmdUtil.GetVmStatus(profile)
if profile == activeProfile {
fmt.Fprintln(display, fmt.Sprintf("- %s\t%s\t(active)", profile, vmStatus))
} else {
fmt.Fprintln(display, fmt.Sprintf("- %s\t%s", profile, vmStatus))
}
}
display.Flush()
}

func init() {
ProfileCmd.AddCommand(profileListCmd)
}
68 changes: 68 additions & 0 deletions cmd/minishift/cmd/profile/profile_set.go
@@ -0,0 +1,68 @@
/*
Copyright (C) 2017 Red Hat, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package profile

import (
"fmt"

"github.com/golang/glog"
cmdUtil "github.com/minishift/minishift/cmd/minishift/cmd/util"
profileActions "github.com/minishift/minishift/pkg/minishift/profile"
"github.com/minishift/minishift/pkg/util/os/atexit"
"github.com/spf13/cobra"
)

const (
unableToSetOcContextErrorMessage = "Make sure the profile is in running state or restart if the problem persists."
)

var profileSetCmd = &cobra.Command{
Use: "set PROFILE_NAME",
Short: "Sets the active profile for Minishift.",
Long: "Sets the active profile for Minishift. After you set the profile, all commands will use the specified profile by default.",
Run: func(cmd *cobra.Command, args []string) {
if len(args) == 0 {
atexit.ExitWithMessage(1, emptyProfileMessage)
} else if len(args) > 1 {
atexit.ExitWithMessage(1, extraArgumentMessage)
}

profileName := args[0]

if !cmdUtil.IsValidProfile(profileName) {
atexit.ExitWithMessage(1, fmt.Sprintf("Error: '%s' is not a valid profile", profileName))
}

err := profileActions.SetActiveProfile(profileName)
if err != nil {
atexit.ExitWithMessage(1, err.Error())
} else {
fmt.Println(fmt.Sprintf("Profile '%s' set as active profile", profileName))
}
err = cmdUtil.SetOcContext(profileName)
if err != nil {
if glog.V(2) {
fmt.Println(fmt.Sprintf("%s", err.Error()))
}
fmt.Println(fmt.Sprintf("oc cli context could not changed for '%s'. %s", profileName, unableToSetOcContextErrorMessage))
}
},
}

func init() {
ProfileCmd.AddCommand(profileSetCmd)
}

0 comments on commit cdf8ac6

Please sign in to comment.