Skip to content

Commit

Permalink
Merge pull request #432 from jrperritt/386
Browse files Browse the repository at this point in the history
'profile activate' and 'profile list' commands
  • Loading branch information
jrperritt committed May 16, 2016
2 parents bf4dafb + 242ecda commit d73788f
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 44 deletions.
25 changes: 25 additions & 0 deletions commandoptions/configfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ func ConfigFile(c *cli.Context, have map[string]Cred, need map[string]string) er
var profile string
if c.IsSet("profile") {
profile = c.String("profile")
} else {
sections, err := ProfileSections()
if err != nil {
return err
}

for _, section := range sections {
if section.KeysHash()["enabled"] == "true" {
profile = section.Name()
}
}
}

section, err := ProfileSection(profile)
Expand Down Expand Up @@ -48,6 +59,20 @@ func ConfigFile(c *cli.Context, have map[string]Cred, need map[string]string) er
return nil
}

func ProfileSections() ([]*ini.Section, error) {
dir, err := util.RackDir()
if err != nil {
return nil, fmt.Errorf("Error retrieving rack directory: %s\n", err)
}
f := path.Join(dir, "config")
cfg, err := ini.Load(f)
if err != nil {
return nil, fmt.Errorf("Error loading config file: %s\n", err)
}
cfg.BlockMode = false
return cfg.Sections(), nil
}

func ProfileSection(profile string) (*ini.Section, error) {
dir, err := util.RackDir()
if err != nil {
Expand Down
23 changes: 3 additions & 20 deletions configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bufio"
"fmt"
"os"
"path"
"strings"

"github.com/rackspace/rack/internal/github.com/codegangsta/cli"
Expand Down Expand Up @@ -39,9 +38,9 @@ func configure(c *cli.Context) {
profile, _ := reader.ReadString('\n')
profile = strings.TrimSpace(profile)

configFile, err := configFile()
configFileLoc, err := util.ConfigFileLocation()
var cfg *ini.File
cfg, err = ini.Load(configFile)
cfg, err = ini.Load(configFileLoc)
if err != nil {
// fmt.Printf("Error loading config file: %s\n", err)
cfg = ini.Empty()
Expand Down Expand Up @@ -82,7 +81,7 @@ func configure(c *cli.Context) {
section.NewKey(key, val)
}

err = cfg.SaveTo(configFile)
err = cfg.SaveTo(configFileLoc)
if err != nil {
//fmt.Printf("Error saving config file: %s\n", err)
return
Expand All @@ -95,19 +94,3 @@ func configure(c *cli.Context) {
}

}

func configFile() (string, error) {
dir, err := util.RackDir()
if err != nil {
return "", fmt.Errorf("Error reading from cache: %s", err)
}
filepath := path.Join(dir, "config")
// check if the cache file exists
if _, err := os.Stat(filepath); err == nil {
return filepath, nil
}
// create the cache file if it doesn't already exist
f, err := os.Create(filepath)
defer f.Close()
return filepath, err
}
35 changes: 26 additions & 9 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Installation and configuration
==============================

This section provides complete and detailed information for installing and configuring the Rackspace Command Line Interface (``rack`` CLI).
This section provides complete and detailed information for installing and configuring the Rackspace Command Line Interface (``rack`` CLI).

Install the CLI
---------------
Expand Down Expand Up @@ -61,9 +61,9 @@ We recommend that you copy the binary to a location outside of your **Downloads*
1. If you don’t already have one, create a new directory for command-line tools. For example, ``C:\tools``.
2. Copy **rack.exe** to that directory.
3. Add the directory to your user's PATH environment variable. You can do this by opening a command prompt window and run the following command::

setx path "%path%;C:\tools"

4. After modifying the PATH variable, open a new command prompt window.

Install the binary with a script
Expand All @@ -79,17 +79,17 @@ Open PowerShell_Ise, paste the following script in the scripting pane, and then

#requires -Version 3
$DownloadPath = 'C:\Tools'

Write-Output -InputObject "[$(Get-Date)] Status :: Set the Tools Directory $DownloadPath"
New-Item -Path $DownloadPath -ItemType Directory -ErrorAction SilentlyContinue > $null
Set-Location -Path $DownloadPath -ErrorAction SilentlyContinue

Write-Output -InputObject "[$(Get-Date)] Status :: Download Rackspace CLI in C:\Tools"
Invoke-WebRequest -Uri 'https://goo.gl/NMvmcx/Windows/amd64/rack.exe' -Method Get -OutFile rack.exe

Write-Output -InputObject "[$(Get-Date)] Status :: Unblock the executable file rack.exe"
Unblock-File -Path $("$DownloadPath\rack.exe")

Write-Output -InputObject "[$(Get-Date)] Status :: Permanently set the path $DownloadPath to the Environment variable (Reboot required)."
[System.Environment]::SetEnvironmentVariable('Path', $env:Path + 'C:\Tools', [System.EnvironmentVariableTarget]::Machine)
Write-Output -InputObject "[$(Get-Date)] Status :: Temporarily set the path $DownloadPath to the Environment variable for immediate use in the current powershell session"
Expand Down Expand Up @@ -127,7 +127,7 @@ The ``configure`` command automatically creates a configuration file for you if
Rackspace API key: <yourRackspaceApiKey>
Rackspace Region: <theRackspaceRegion>
Profile Name (leave blank to create a default profile):

Username is the username for your Rackspace Cloud account. You can get your API key by logging in to the Cloud Control Panel, clicking on your account name in the upper-right corner, and then selecting **Account Settings**. The region is the region where your Rackspace infrastructure is deployed. If you want to create a profile other than the default profile, enter a name for the profile.

After the profile is created, you can immediately start working. For example, you could issue the following command to get a list of the servers on your Rackspace account::
Expand All @@ -148,7 +148,7 @@ environment variables).
Configuration file
^^^^^^^^^^^^^^^^^^

Any authentication parameters not set on the command line are looked for in a configuration file. The configuration file should be located in ``$HOME/.rack/config``. When you use the interactive ``configure`` command, a configuration file is automatically created.
Any authentication parameters not set on the command line are looked for in a configuration file. The configuration file should be located in ``$HOME/.rack/config``. When you use the interactive ``configure`` command, a configuration file is automatically created.

The configuration file format is similar to the following format::

Expand All @@ -168,6 +168,11 @@ The preceding example shows a default profile that doesn't have a named section.

Note that none of the authentication parameters have to be set in the configuration file. Parameters not set there are looked for elsewhere.

To list all the profiles in the config file::

rack profile list


Environment variables
^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -227,8 +232,20 @@ For example::

In addition, you can provide it as a flag on the command-line or as a value in the configuration file profile. In either case, the parameter name is ``auth-url``.

Activating a profile
^^^^^^^^^^^^^^^^^^^^

If you find yourself running several commands in a profile other than the one designated as the default, you can set a flag in your default profile to enable
a command to activate a profile from the command-line.
Adding::

enable-profile-activate = true

to your default profile will allow you to use the `profile activate` command. For example, if you have a profile named ``ord``::

rack profile activate --name ord

would run all future commands with the configuration values under that profile until `profile activate` is called again with a different profile.

.. _go: https://golang.org/
.. _Mac OS X (64-bit): https://ec4a542dbf90c03b9f75-b342aba65414ad802720b41e8159cf45.ssl.cf5.rackcdn.com/1.1.1/Darwin/amd64/rack
Expand Down
37 changes: 22 additions & 15 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,62 +43,69 @@ func Usage() string {

// Desc returns, you guessed it, the description
func Desc() string {
return `The rack CLI manages authentication, configures a local setup, and provides workflows for operations on Rackspace Cloud resources.`
return `The rack CLI manages authentication, configures a local setup, and provides workflows for operations on Rackspace Cloud resources`
}

// Cmds returns a list of commands supported by the tool
func Cmds() []cli.Command {
canActivateProfile := util.CanActivateProfile()

return []cli.Command{
{
Name: "configure",
Usage: "Interactively create a config file for Rackspace authentication.",
Usage: "Interactively create a config file for Rackspace authentication",
Action: configure,
},
{
Name: "init",
Usage: strings.Join([]string{"Enable tab for command completion.",
"\tFor Linux and OS X, creates the `rack` man page and sets up",
"\tcommand completion for the Bash shell. Run `man ./rack.1` to",
"\tview the generated man page.",
"\tFor Windows, creates a `posh_autocomplete.ps1` file in the",
"\t`$HOME/.rack` directory. You must run the file to set up",
"\tcommand completion."}, "\n"),
Usage: "Enable tab for command completion.\n" +
"\tFor Linux and OS X, creates the `rack` man page and sets up\n" +
"\tcommand completion for the Bash shell. Run `man ./rack.1` to\n" +
"\tview the generated man page.\n" +
"\tFor Windows, creates a `posh_autocomplete.ps1` file in the\n" +
"\t`$HOME/.rack` directory. You must run the file to set up\n" +
"\tcommand completion\n",
Action: func(c *cli.Context) {
setup.Init(c)
man()
},
},
{
Name: "version",
Usage: "Print the version of this binary.",
Usage: "Print the version of this binary",
Action: func(c *cli.Context) {
fmt.Fprintf(c.App.Writer, "%v version %v\ncommit: %v\n", c.App.Name, util.Version, util.Commit)
},
},
{
Name: "profile",
Usage: "Used to perform operations on user profiles",
Subcommands: profileCommandsGet(canActivateProfile),
},
{
Name: "servers",
Usage: "Operations on cloud servers, both virtual and bare metal.",
Usage: "Operations on cloud servers, both virtual and bare metal",
Subcommands: serverscommands.Get(),
},
{
Name: "files",
Usage: "Object storage for files and media.",
Usage: "Object storage for files and media",
Subcommands: filescommands.Get(),
},
{
Name: "networks",
Usage: "Software-defined networking.",
Usage: "Software-defined networking",
Subcommands: networkscommands.Get(),
},
{
Name: "block-storage",
Usage: strings.Join([]string{"Block-level storage, exposed as volumes to mount to",
"\thost servers. Work with volumes and their associated snapshots."}, "\n"),
"\thost servers. Work with volumes and their associated snapshots"}, "\n"),
Subcommands: blockstoragecommands.Get(),
},
{
Name: "orchestration",
Usage: "Use a template language to orchestrate Rackspace cloud services.",
Usage: "Use a template language to orchestrate Rackspace cloud services",
Subcommands: orchestrationcommands.Get(),
},
}
Expand Down
126 changes: 126 additions & 0 deletions profile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package main

import (
"fmt"

"github.com/rackspace/rack/commandoptions"
"github.com/rackspace/rack/internal/github.com/codegangsta/cli"
"github.com/rackspace/rack/internal/gopkg.in/ini.v1"
"github.com/rackspace/rack/util"
)

var commandActivate = cli.Command{
Name: "activate",
Description: "Activate a profile. Activating a profile will have the following\n" +
"\torder for the way that `rack` looks for command configuration values:\n" +
"\t1. command-line options\n" +
"\t2. the active profile\n" +
"\t3. the default profile\n" +
"\t4. environment variables\n" +
"\n" +
"\tNOTE: The safest way to use `rack` is by always explicitly providing\n" +
"\tconfiguration values (like `--profile`) as command-line options. Running\n" +
"\ta command without knowing which profile is active can result in unintended\n" +
"\tconsequences.",
Usage: "rack profile activate --name <profile-name>",
Action: profileActivate,
Flags: profileFlagsActivate,
BashComplete: func(c *cli.Context) {
commandoptions.CompleteFlags(profileFlagsActivate)
},
}

var commandList = cli.Command{
Name: "list",
Description: "List profile information",
Usage: "rack profile list",
Action: profileList,
}

var commandsProfile = []cli.Command{
commandList,
}

var commandsProfileAdmin = []cli.Command{
commandActivate,
}

func profileCommandsGet(canActivateProfile bool) []cli.Command {
if canActivateProfile {
return append(commandsProfile, commandsProfileAdmin...)
}
return commandsProfile
}

var profileFlagsActivate = []cli.Flag{
cli.StringFlag{
Name: "name",
Usage: "[required] The name of the profile to activate",
},
}

func profileActivate(c *cli.Context) {
if !c.IsSet("name") {
fmt.Fprintln(c.App.Writer, "You must provide the profile name with the `name` flag")
return
}

profileName := c.String("name")

configFileLoc, err := util.ConfigFileLocation()
if err != nil {
fmt.Fprintf(c.App.Writer, "Error to determining config file location: %s\n", err)
return
}

cfg, err := ini.Load(configFileLoc)
if err != nil {
fmt.Fprintf(c.App.Writer, "Error loading config file: %s\n", err)
return
}

chosenSection, err := cfg.GetSection(profileName)
if err != nil {
fmt.Fprintf(c.App.Writer, "Section [%s] doesn't exist in config file\n", profileName)
return
}

sections := cfg.Sections()
for _, section := range sections {
section.DeleteKey("enabled")
}

chosenSection.Key("enabled").SetValue("true")

err = cfg.SaveTo(configFileLoc)
if err != nil {
fmt.Fprintf(c.App.Writer, "Error saving config file: %s\n", err)
return
}

fmt.Fprintf(c.App.Writer, "Successfully activated profile [%s]\n", profileName)
}

func profileList(c *cli.Context) {
configFileLoc, err := util.ConfigFileLocation()
if err != nil {
fmt.Fprintf(c.App.Writer, "Error to determining config file location: %s\n", err)
return
}

cfg, err := ini.Load(configFileLoc)
if err != nil {
fmt.Fprintf(c.App.Writer, "Error loading config file: %s\n", err)
return
}

sections := cfg.Sections()
for _, section := range sections {
fmt.Fprintf(c.App.Writer, "[%s]\n", section.Name())
for k, v := range section.KeysHash() {
fmt.Fprintf(c.App.Writer, "%s = %s\n", k, v)
}
fmt.Fprintln(c.App.Writer)
fmt.Fprintln(c.App.Writer)
}
}
Loading

0 comments on commit d73788f

Please sign in to comment.