Skip to content

Commit

Permalink
Merge pull request #101 from thebsdbox/context
Browse files Browse the repository at this point in the history
Added login contexts, allows multiple UCP sessions
  • Loading branch information
thebsdbox authored Aug 10, 2018
2 parents 2de3e5b + 46ffb9d commit 61fc6d6
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 49 deletions.
35 changes: 1 addition & 34 deletions cmd/ucp.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package cmd

import (
"fmt"
"os"
"strings"

"github.com/spf13/cobra"
"github.com/thebsdbox/diver/pkg/ucp"
Expand All @@ -16,14 +14,6 @@ var importPath, exportPath, action string
var top, exampleFile bool

func init() {
ucpLogin.Flags().StringVar(&ucpClient.Username, "username", os.Getenv("UCP_USERNAME"), "Username that has permissions to authenticate to Docker EE")
ucpLogin.Flags().StringVar(&ucpClient.Password, "password", os.Getenv("UCP_PASSWORD"), "Password allowing a user to authenticate to Docker EE")
ucpLogin.Flags().StringVar(&ucpClient.UCPURL, "url", os.Getenv("UCP_URL"), "URL for Docker Universal Control Plane, e.g. https://10.0.0.1")
ignoreCert := strings.ToLower(os.Getenv("UCP_INSECURE")) == "true"

ucpLogin.Flags().BoolVar(&ucpClient.IgnoreCert, "ignorecert", ignoreCert, "Ignore x509 certificate")

UCPRoot.AddCommand(ucpLogin)

// Add UCP and subcommands to the main application
diverCmd.AddCommand(UCPRoot)
Expand All @@ -40,7 +30,7 @@ var UCPRoot = &cobra.Command{
if err != nil {
// Fatal error if can't read the token
cmd.Help()
log.Warn("Unable to find existing session, please login")
log.Errorf("%v", err)
return
}
currentAccount, err := existingClient.AuthStatus()
Expand All @@ -55,26 +45,3 @@ var UCPRoot = &cobra.Command{
return
},
}

// UCPRoot - This is the root of all UCP commands / flags
var ucpLogin = &cobra.Command{
Use: "login",
Short: "Authenticate against the Universal Control Pane",
Run: func(cmd *cobra.Command, args []string) {
log.SetLevel(log.Level(logLevel))

err := ucpClient.Connect()

// Check if connection was succesful
if err != nil {
log.Fatalf("%v", err)
} else {
// If succesfull write the token and annouce as succesful
err = ucpClient.WriteToken()
if err != nil {
log.Errorf("%v", err)
}
log.Infof("Succesfully logged into [%s]", ucpClient.UCPURL)
}
},
}
92 changes: 92 additions & 0 deletions cmd/ucp_login.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package cmd

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

log "github.com/Sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/thebsdbox/diver/pkg/ucp"
)

var session int

func init() {
diverCmd.AddCommand(UCPRoot)

ucpLogin.Flags().StringVar(&ucpClient.Username, "username", os.Getenv("UCP_USERNAME"), "Username that has permissions to authenticate to Docker EE")
ucpLogin.Flags().StringVar(&ucpClient.Password, "password", os.Getenv("UCP_PASSWORD"), "Password allowing a user to authenticate to Docker EE")
ucpLogin.Flags().StringVar(&ucpClient.UCPURL, "url", os.Getenv("UCP_URL"), "URL for Docker EE, e.g. https://10.0.0.1")
ignoreCert := strings.ToLower(os.Getenv("UCP_INSECURE")) == "true"

ucpLogin.Flags().BoolVar(&ucpClient.IgnoreCert, "ignorecert", ignoreCert, "Ignore x509 certificate")

ucpLoginSet.Flags().IntVar(&session, "id", 0, "The session ID to set to active")

ucpLogin.AddCommand(ucpLoginList)
ucpLogin.AddCommand(ucpLoginSet)

UCPRoot.AddCommand(ucpLogin)
}

// UCPLogin - This manages logging in and swapping of contexts
var ucpLogin = &cobra.Command{
Use: "login",
Short: "Authenticate against the Universal Control Pane",
Run: func(cmd *cobra.Command, args []string) {
log.SetLevel(log.Level(logLevel))

err := ucpClient.Connect()

// Check if connection was succesful
if err != nil {
cmd.Help()
log.Fatalf("%v", err)
} else {
// If succesfull write the token and annouce as succesful
err = ucpClient.WriteToken()
if err != nil {
log.Errorf("%v", err)
}
log.Infof("Succesfully logged into [%s]", ucpClient.UCPURL)
}
},
}

// ucpLoginList - This manages logging in and swapping of contexts
var ucpLoginList = &cobra.Command{
Use: "list",
Short: "List all UCP sessions",
Run: func(cmd *cobra.Command, args []string) {
log.SetLevel(log.Level(logLevel))
// Retrieve all of the client sessions in the token file
clientTokens, err := ucp.ReadAllClients()
if err != nil {
log.Errorf("%v", err)
}

w := tabwriter.NewWriter(os.Stdout, 0, 0, tabPadding, ' ', 0)
fmt.Fprintln(w, "ID\tAddress\tActive")
for i := range clientTokens {
fmt.Fprintf(w, "%d\t%s\t%t\n", i, clientTokens[i].UCPAddress, clientTokens[i].Active)
}
w.Flush()
},
}

// ucpLoginSet - This manages logging in and swapping of contexts
var ucpLoginSet = &cobra.Command{
Use: "setActive",
Short: "Set the active UCP session",
Run: func(cmd *cobra.Command, args []string) {
log.SetLevel(log.Level(logLevel))

err := ucp.SetActiveSession(session)
if err != nil {
log.Fatalf("%v", err)
}
log.Infof("Set session [%d] to active", session)
},
}
22 changes: 22 additions & 0 deletions docs/ucp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,28 @@ INFO[0000] Succesfully logged into [https://docker01.fnnrn.me]

**Note** the `login` command will create a `~/.ucptoken` file that is used for all further `diver` commands. In the event that login errors start to occur, or logins fail check the permissions of this file or alternatively remove this file.

### Working with multiple UCP sessions

When you log into a new UCP server, then a new session will be created. To view the sessions that are available use the following command:

```
$ ./diver ucp login list
ID Address Active
0 https://192.168.0.140 true
1 https://docker01.fnnrn.me false
```

To set a different session to active, you can use the setActive command to change the active session.

```
./diver ucp login setActive --id 1
INFO[0000] Set session [1] to active
./diver ucp login list
ID Address Active
0 https://192.168.0.140 false
1 https://docker01.fnnrn.me true
```


### Checking access

Expand Down
126 changes: 111 additions & 15 deletions pkg/ucp/ucpClient.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (c *Client) Connect() error {
if err != nil {
return err
}
log.Debugf("%v", string(response))
log.Debugf("%s", response)

var responseData map[string]interface{}
err = json.Unmarshal(response, &responseData)
Expand Down Expand Up @@ -330,10 +330,12 @@ func (c *Client) doRequest(req *http.Request) ([]byte, error) {
}
}

type internal struct {
//ClientSession - returns everything needed to interact with UCP
type ClientSession struct {
UCPAddress string `json:"address"`
Token string `json:"token"`
IgnoreCert bool `json:"ignoreCert"`
Active bool `json:"active"`
}

// WriteToken - Writes a copy of the token to the
Expand All @@ -343,17 +345,48 @@ func (c *Client) WriteToken() error {
return fmt.Errorf("Not logged in, or no UCP token present")
}

// Retrieve existing tokens, to update an existing or append a new session

clientTokens, err := ReadAllClients()
if err != nil {
// An error here could be related to no existing login or corrupted file
log.Debugf("%v", err)
}

// build path
path := fmt.Sprintf("%s/.ucptoken", os.Getenv("HOME"))
log.Debugf("Writing Token to [%s]", path)

clientToken := internal{
clientToken := ClientSession{
UCPAddress: c.UCPURL,
Token: c.Token,
IgnoreCert: c.IgnoreCert,
Active: true,
}

var found bool

for i := range clientTokens {
// Ensure all tokens are disabled
clientTokens[i].Active = false
if clientTokens[i].UCPAddress == c.UCPURL {
// If the session already exists update the token
clientTokens[i].Token = c.Token
// Enable this one as it has been updated
clientTokens[i].Active = true

found = true
log.Infoln("Updating existing session")
}
}

b, err := json.Marshal(clientToken)
if found != true {
clientTokens = append(clientTokens, clientToken)
log.Infoln("Adding new session")

}

b, err := json.Marshal(clientTokens)
if err != nil {
return err
}
Expand All @@ -364,28 +397,91 @@ func (c *Client) WriteToken() error {
return nil
}

// ReadToken - Reads the token from a file
func ReadToken() (*Client, error) {
// build path
// ReadAllClients - This will read the token file and return all of the clients
func ReadAllClients() ([]ClientSession, error) {

var clientArray []ClientSession
path := fmt.Sprintf("%s/.ucptoken", os.Getenv("HOME"))
log.Debugf("Reading Token from [%s]", path)
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("No Session Token could be found, please login")
}

clientToken := internal{}

err = json.Unmarshal(data, &clientToken)
err = json.Unmarshal(data, &clientArray)
if err != nil {
return nil, fmt.Errorf("Corrupted Session Token, please login")
}

client := &Client{
UCPURL: clientToken.UCPAddress,
Token: clientToken.Token,
IgnoreCert: clientToken.IgnoreCert,
return clientArray, nil
}

// ReadToken - Reads the token from a file
func ReadToken() (*Client, error) {

// Retrieve all of the client sessions in the token file
clientTokens, err := ReadAllClients()
if err != nil {
return nil, err
}

if len(clientTokens) == 0 {
return nil, fmt.Errorf("No sessions found, please login")
}

// Find the active session and return it
for i := range clientTokens {
if clientTokens[i].Active == true {
client := &Client{
UCPURL: clientTokens[i].UCPAddress,
Token: clientTokens[i].Token,
IgnoreCert: clientTokens[i].IgnoreCert,
}
return client, nil

}
}

return nil, fmt.Errorf("No active sessions found, please login")

}

// SetActiveSession - this will set the active session
func SetActiveSession(sessionID int) error {
// Retrieve existing tokens, to update an existing or append a new session

clientTokens, err := ReadAllClients()
if err != nil {
// An error here could be related to no existing login or corrupted file
log.Debugf("%v", err)
}

// Arrays begin at 0
if (len(clientTokens) - 1) < sessionID {
return fmt.Errorf("Session ID [%d] doesn't exist", sessionID)
}

// build path
path := fmt.Sprintf("%s/.ucptoken", os.Getenv("HOME"))
log.Debugf("Writing Token to [%s]", path)

for i := range clientTokens {
// Ensure all tokens are disabled
clientTokens[i].Active = false
if i == sessionID {
// Enable this one as it has been updated
clientTokens[i].Active = true
}
}

b, err := json.Marshal(clientTokens)
if err != nil {
return err
}
err = ioutil.WriteFile(path, b, 0644)
if err != nil {
return err
}
return nil

return client, nil
}

0 comments on commit 61fc6d6

Please sign in to comment.