Skip to content
This repository has been archived by the owner on Nov 16, 2020. It is now read-only.

Commit

Permalink
Add Organization CLI and fix tenancy issues
Browse files Browse the repository at this point in the history
* Adds CLI for managing organizations
* Adds a flag for specifying global policies (policies that work
  across organizations)
* Update CHANGELOG
* Misc fixes for handling service accounts in CLI
  • Loading branch information
neosab committed Jun 19, 2018
1 parent fc4f43c commit 4932107
Show file tree
Hide file tree
Showing 20 changed files with 292 additions and 33 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ All notable changes to this project will be documented in this file. For more in

### Fixed


- **Limited Multi-Tenancy Support.** Dispatch now supports multi-tenancy with the ability to create multiple organizations and define policies to gives access to users on those organizations.
This is a limited functionality and does not isolate the function execution environments within the underlying FaaS engine or define network policies for the function pods running on Kubernetes.[PR #510](https://github.com/vmware/dispatch/pull/510) [PR #529](https://github.com/vmware/dispatch/pull/529).
- **Fix single file Java support.** Previously Java would fail for single files if no handler argument is provided. [PR #517](https://github.com/vmware/dispatch/pull/517)
- [[Issue #483](https://github.com/vmware/dispatch/issues/483)] **No helpful error output when an error occurs during function create.**
Added a `reason` field to the function object to hold function creation errors. [PR #513](https://github.com/vmware/dispatch/pull/513)
Expand Down
2 changes: 1 addition & 1 deletion pkg/dispatchcli/cmd/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func GetAuthInfoWriter() runtime.ClientAuthInfoWriter {
issuer := dispatchConfig.ServiceAccount
if len(strings.SplitN(dispatchConfig.ServiceAccount, "/", 2)) == 1 {
// Missing org info
issuer = fmt.Sprintf("%s/%s", getOrganization(), dispatchConfig.ServiceAccount)
issuer = fmt.Sprintf("%s/%s", getOrgFromConfig(), dispatchConfig.ServiceAccount)
}
token, err := generateAndSignJWToken(issuer, nil, &dispatchConfig.JWTPrivateKey)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/dispatchcli/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func resourceName(name string) string {

}

func getOrganization() string {
func getOrgFromConfig() string {
if dispatchConfig.Organization == "" {
fatal(fmt.Sprintf("error: missing organization. Please specify it using --organization flag or set in the config file %s. "+
"If this is a new Dispatch installation, you can check `dispatch manage bootstrap --help` command for initial setup.", viper.ConfigFileUsed()), 1)
Expand Down
2 changes: 1 addition & 1 deletion pkg/dispatchcli/cmd/create_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func CallCreateApplication(i interface{}) error {
params := &application.AddAppParams{
Body: body,
Context: context.Background(),
XDispatchOrg: getOrganization(),
XDispatchOrg: getOrgFromConfig(),
}

created, err := client.Application.AddApp(params, GetAuthInfoWriter())
Expand Down
2 changes: 1 addition & 1 deletion pkg/dispatchcli/cmd/delete_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func CallDeleteApplication(i interface{}) error {
params := &application.DeleteAppParams{
Application: *applicationModel.Name,
Context: context.Background(),
XDispatchOrg: getOrganization(),
XDispatchOrg: getOrgFromConfig(),
}

deleted, err := client.Application.DeleteApp(params, GetAuthInfoWriter())
Expand Down
4 changes: 2 additions & 2 deletions pkg/dispatchcli/cmd/get_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func getApplication(out, errOut io.Writer, cmd *cobra.Command, args []string) er
params := &application.GetAppParams{
Context: context.Background(),
Application: args[0],
XDispatchOrg: getOrganization(),
XDispatchOrg: getOrgFromConfig(),
}

resp, err := client.Application.GetApp(params, GetAuthInfoWriter())
Expand All @@ -67,7 +67,7 @@ func getApplications(out, errOut io.Writer, cmd *cobra.Command) error {
client := applicationManagerClient()
params := &application.GetAppsParams{
Context: context.Background(),
XDispatchOrg: getOrganization(),
XDispatchOrg: getOrgFromConfig(),
}
resp, err := client.Application.GetApps(params, GetAuthInfoWriter())
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions pkg/dispatchcli/cmd/iam_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ func NewCmdIamCreate(out io.Writer, errOut io.Writer) *cobra.Command {
}
cmd.AddCommand(NewCmdIamCreatePolicy(out, errOut))
cmd.AddCommand(NewCmdIamCreateServiceAccount(out, errOut))
cmd.AddCommand(NewCmdIamCreateOrganization(out, errOut))
return cmd
}
74 changes: 74 additions & 0 deletions pkg/dispatchcli/cmd/iam_create_organization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
///////////////////////////////////////////////////////////////////////
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
///////////////////////////////////////////////////////////////////////

package cmd

import (
"context"
"fmt"
"io"

"github.com/spf13/cobra"
"github.com/vmware/dispatch/pkg/client"

"github.com/vmware/dispatch/pkg/api/v1"
"github.com/vmware/dispatch/pkg/dispatchcli/i18n"
)

var (
createOrganizationLong = i18n.T(`Create a dispatch organization`)

createOrganizationExample = i18n.T(`
# Create a organization
dispatch iam create organization <organization_name>
`)
)

// NewCmdIamCreateOrganization creates command responsible for org creation
func NewCmdIamCreateOrganization(out, errOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: i18n.T(`organization ORGANIZATION_NAME`),
Short: i18n.T(`Create organization`),
Long: createOrganizationLong,
Example: createOrganizationExample,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
c := identityManagerClient()
err := createOrganization(out, errOut, cmd, args, c)
CheckErr(err)
},
}
return cmd
}

// CallCreateOrganization makes the api call to create a organization
func callCreateOrganization(c client.IdentityClient) ModelAction {
return func(p interface{}) error {
organizationModel := p.(*v1.Organization)

created, err := c.CreateOrganization(context.TODO(), "", organizationModel)
if err != nil {
return err
}

*organizationModel = *created
return nil
}
}

func createOrganization(out, errOut io.Writer, cmd *cobra.Command, args []string, c client.IdentityClient) error {
organizationName := args[0]

organizationModel := &v1.Organization{
Name: &organizationName,
}

err := callCreateOrganization(c)(organizationModel)
if err != nil {
return err
}
fmt.Fprintf(out, "Created organization: %s\n", *organizationModel.Name)
return nil
}
7 changes: 5 additions & 2 deletions pkg/dispatchcli/cmd/iam_create_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ dispatch iam create policy example_policy --subject [email protected] --subject
subjects *[]string
actions *[]string
resources *[]string
global *bool
)

// NewCmdIamCreatePolicy creates command responsible for dispatch policy creation
Expand All @@ -53,6 +54,7 @@ func NewCmdIamCreatePolicy(out io.Writer, errOut io.Writer) *cobra.Command {
subjects = cmd.Flags().StringSliceP("subject", "s", []string{""}, "subjects of policy rule, separated by comma")
actions = cmd.Flags().StringSliceP("action", "a", []string{""}, "actions of policy rule, separated by comma")
resources = cmd.Flags().StringSliceP("resource", "r", []string{""}, "resources of policy rule, separated by comma")
global = cmd.Flags().Bool("global", false, "applies the policy globally across all organizations")
return cmd
}

Expand Down Expand Up @@ -82,8 +84,9 @@ func createPolicy(out, errOut io.Writer, cmd *cobra.Command, args []string, c cl
}

policyModel := &v1.Policy{
Name: &policyName,
Rules: policyRules,
Name: &policyName,
Rules: policyRules,
Global: *global,
}

err := CallCreatePolicy(c)(policyModel)
Expand Down
1 change: 1 addition & 0 deletions pkg/dispatchcli/cmd/iam_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ func NewCmdIamDelete(out io.Writer, errOut io.Writer) *cobra.Command {
}
cmd.AddCommand(NewCmdIamDeletePolicy(out, errOut))
cmd.AddCommand(NewCmdIamDeleteServiceAccount(out, errOut))
cmd.AddCommand(NewCmdIamDeleteOrganization(out, errOut))
return cmd
}
74 changes: 74 additions & 0 deletions pkg/dispatchcli/cmd/iam_delete_organization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
///////////////////////////////////////////////////////////////////////
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
///////////////////////////////////////////////////////////////////////

package cmd

import (
"context"
"encoding/json"
"fmt"
"io"

"github.com/spf13/cobra"
"github.com/vmware/dispatch/pkg/client"

"github.com/vmware/dispatch/pkg/api/v1"
"github.com/vmware/dispatch/pkg/dispatchcli/i18n"
)

var (
deleteOrganizationLong = i18n.T(`Delete a dispatch organization`)

// TODO: add examples
deleteOrganizationExample = i18n.T(``)
)

// NewCmdIamDeleteOrganization creates command for delete service accounts
func NewCmdIamDeleteOrganization(out, errOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: i18n.T("organization ORGANIZATION_NAME"),
Short: i18n.T("Delete organization"),
Long: deleteOrganizationLong,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
c := identityManagerClient()
err := deleteOrganization(out, errOut, cmd, args, c)
CheckErr(err)
},
}
return cmd
}

// CallDeleteOrganization makes the API call to delete Organization
func CallDeleteOrganization(c client.IdentityClient) ModelAction {
return func(s interface{}) error {
organizationModel := s.(*v1.Organization)

deleted, err := c.DeleteOrganization(context.TODO(), "", *organizationModel.Name)
if err != nil {
return err
}
*organizationModel = *deleted
return nil
}
}

func deleteOrganization(out, errOut io.Writer, cmd *cobra.Command, args []string, c client.IdentityClient) error {
organizationModel := v1.Organization{
Name: &args[0],
}

err := CallDeleteOrganization(c)(&organizationModel)
if err != nil {
return err
}
if dispatchConfig.JSON {
encoder := json.NewEncoder(out)
encoder.SetIndent("", " ")
return encoder.Encode(organizationModel)
}
fmt.Fprintf(out, "Deleted Organization: %s\n", *organizationModel.Name)
return nil
}
1 change: 1 addition & 0 deletions pkg/dispatchcli/cmd/iam_get.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ func NewCmdIamGet(out io.Writer, errOut io.Writer) *cobra.Command {
}
cmd.AddCommand(NewCmdIamGetPolicy(out, errOut))
cmd.AddCommand(NewCmdIamGetServiceAccount(out, errOut))
cmd.AddCommand(NewCmdIamGetOrganization(out, errOut))
return cmd
}
90 changes: 90 additions & 0 deletions pkg/dispatchcli/cmd/iam_get_organization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
///////////////////////////////////////////////////////////////////////
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
///////////////////////////////////////////////////////////////////////

package cmd

import (
"context"
"encoding/json"
"io"
"time"

"github.com/olekukonko/tablewriter"
"github.com/spf13/cobra"
"github.com/vmware/dispatch/pkg/client"

"github.com/vmware/dispatch/pkg/api/v1"
"github.com/vmware/dispatch/pkg/dispatchcli/i18n"
)

var (
getOrganizationsLong = i18n.T(`Get organizations`)

// TODO: examples
getOrganizationsExample = i18n.T(``)
)

// NewCmdIamGetOrganization creates command for getting organizations
func NewCmdIamGetOrganization(out, errOut io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: i18n.T("organization [ORGANIZATION_NAME]"),
Short: i18n.T("Get organizations"),
Long: getOrganizationsLong,
Args: cobra.MaximumNArgs(1),
Aliases: []string{"organizations"},
Run: func(cmd *cobra.Command, args []string) {
var err error
c := identityManagerClient()
if len(args) > 0 {
err = getOrganization(out, errOut, cmd, args, c)
} else {
err = getOrganizations(out, errOut, cmd, c)
}
CheckErr(err)
},
}
return cmd
}

func getOrganization(out, errOut io.Writer, cmd *cobra.Command, args []string, c client.IdentityClient) error {
resp, err := c.GetOrganization(context.TODO(), "", args[0])
if err != nil {
return err
}

return formatOrganizationOutput(out, false, []v1.Organization{*resp})
}

func getOrganizations(out, errOut io.Writer, cmd *cobra.Command, c client.IdentityClient) error {
resp, err := c.ListOrganizations(context.TODO(), "")
if err != nil {
return err
}
return formatOrganizationOutput(out, true, resp)
}

func formatOrganizationOutput(out io.Writer, list bool, organizations []v1.Organization) error {

if dispatchConfig.JSON {
encoder := json.NewEncoder(out)
encoder.SetIndent("", " ")
if list {
return encoder.Encode(organizations)
}
return encoder.Encode(organizations[0])
}

headers := []string{"Name", "Created Date"}
table := tablewriter.NewWriter(out)
table.SetHeader(headers)
table.SetBorders(tablewriter.Border{Left: false, Top: false, Right: false, Bottom: false})
table.SetCenterSeparator("")
for _, organization := range organizations {
row := []string{*organization.Name, time.Unix(organization.CreatedTime, 0).Local().Format(time.UnixDate)}
table.Append(row)
}
table.Render()
return nil
}
5 changes: 3 additions & 2 deletions pkg/dispatchcli/cmd/iam_get_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"encoding/json"
"fmt"
"io"
"strconv"
"time"

"github.com/olekukonko/tablewriter"
Expand Down Expand Up @@ -79,7 +80,7 @@ func formatPolicyOutput(out io.Writer, list bool, policies []v1.Policy) error {
return encoder.Encode(policies[0])
}

headers := []string{"Name", "Created Date"}
headers := []string{"Name", "Global", "Created Date"}
if printRuleContent {
headers = append(headers, "Rules")
} else {
Expand All @@ -94,7 +95,7 @@ func formatPolicyOutput(out io.Writer, list bool, policies []v1.Policy) error {
for _, policy := range policies {
// For now, a policy has one rule
ruleContent, err := json.MarshalIndent(policy.Rules[0], "", " ")
row := []string{*policy.Name, time.Unix(policy.CreatedTime, 0).Local().Format(time.UnixDate)}
row := []string{*policy.Name, strconv.FormatBool(policy.Global), time.Unix(policy.CreatedTime, 0).Local().Format(time.UnixDate)}
if printRuleContent && err == nil {
row = append(row, string(ruleContent))
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/dispatchcli/cmd/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func oidcLogin(in io.Reader, out, errOut io.Writer, cmd *cobra.Command, args []s
// Login Dispatch by service account
func serviceAccountLogin(in io.Reader, out, errOut io.Writer, cmd *cobra.Command, args []string) (err error) {

_, err = identityManagerClient().Home(context.TODO(), getOrganization())
_, err = identityManagerClient().Home(context.TODO(), getOrgFromConfig())
if err != nil {
return errors.Wrap(err, "error logging in")
}
Expand Down
Loading

0 comments on commit 4932107

Please sign in to comment.