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

Add Organization CLI and fix tenancy issues #529

Merged
merged 2 commits into from
Jun 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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