cli/cmd/project.go

388 lines
8.7 KiB
Go

package cmd
import (
"fmt"
"io"
"github.com/rancher/cli/cliclient"
"github.com/rancher/norman/types"
managementClient "github.com/rancher/rancher/pkg/client/generated/management/v3"
"github.com/urfave/cli"
)
type ProjectData struct {
ID string
Project managementClient.Project
}
func ProjectCommand() cli.Command {
return cli.Command{
Name: "projects",
Aliases: []string{"project"},
Usage: "Operations on projects",
Action: defaultAction(projectLs),
Subcommands: []cli.Command{
{
Name: "ls",
Usage: "List projects",
Description: "\nLists all projects in the current cluster.",
ArgsUsage: "None",
Action: projectLs,
Flags: []cli.Flag{
cli.StringFlag{
Name: "format",
Usage: "'json', 'yaml' or Custom format: '{{.Project.ID}} {{.Project.Name}}'",
},
quietFlag,
},
},
{
Name: "create",
Usage: "Create a project",
Description: "\nCreates a project in the current cluster.",
ArgsUsage: "[NEWPROJECTNAME...]",
Action: projectCreate,
Flags: []cli.Flag{
cli.StringFlag{
Name: "cluster",
Usage: "Cluster ID to create the project in",
},
cli.StringFlag{
Name: "description",
Usage: "Description to apply to the project",
},
},
},
{
Name: "delete",
Aliases: []string{"rm"},
Usage: "Delete a project by ID",
ArgsUsage: "[PROJECTID PROJECTNAME]",
Action: projectDelete,
},
{
Name: "add-member-role",
Usage: "Add a member to the project",
Action: addProjectMemberRoles,
Description: "Examples:\n #Create the roles of 'create-ns' and 'services-manage' for a user named 'user1'\n rancher project add-member-role user1 create-ns services-manage\n",
ArgsUsage: "[USERNAME, ROLE...]",
Flags: []cli.Flag{
cli.StringFlag{
Name: "project-id",
Usage: "Optional project ID to apply this change to, defaults to the current context",
},
},
},
{
Name: "delete-member-role",
Usage: "Delete a member from the project",
Action: deleteProjectMemberRoles,
Description: "Examples:\n #Delete the roles of 'create-ns' and 'services-manage' for a user named 'user1'\n rancher project delete-member-role user1 create-ns services-manage\n",
ArgsUsage: "[USERNAME, ROLE...]",
Flags: []cli.Flag{
cli.StringFlag{
Name: "project-id",
Usage: "Optional project ID to apply this change to, defaults to the current context",
},
},
},
{
Name: "list-roles",
Usage: "List all available roles for a project",
Action: listProjectRoles,
},
{
Name: "list-members",
Usage: "List current members of the project",
Action: func(cctx *cli.Context) error {
client, err := GetClient(cctx)
if err != nil {
return err
}
return listProjectMembers(
cctx,
cctx.App.Writer,
client.UserConfig,
client.ManagementClient.ProjectRoleTemplateBinding,
client.ManagementClient.Principal,
)
},
Flags: []cli.Flag{
cli.StringFlag{
Name: "project-id",
Usage: "Optional project ID to list members for, defaults to the current context",
},
cli.StringFlag{
Name: "format",
Usage: "'json', 'yaml' or Custom format: '{{.ID }} {{.Member }}'",
},
quietFlag,
},
},
},
}
}
func projectLs(ctx *cli.Context) error {
c, err := GetClient(ctx)
if err != nil {
return err
}
collection, err := getProjectList(ctx, c)
if err != nil {
return err
}
writer := NewTableWriter([][]string{
{"ID", "ID"},
{"NAME", "Project.Name"},
{"STATE", "Project.State"},
{"DESCRIPTION", "Project.Description"},
}, ctx)
defer writer.Close()
for _, item := range collection.Data {
writer.Write(&ProjectData{
ID: item.ID,
Project: item,
})
}
return writer.Err()
}
func projectCreate(ctx *cli.Context) error {
if ctx.NArg() == 0 {
return cli.ShowSubcommandHelp(ctx)
}
c, err := GetClient(ctx)
if err != nil {
return err
}
clusterID := c.UserConfig.FocusedCluster()
if ctx.String("cluster") != "" {
resource, err := Lookup(c, ctx.String("cluster"), "cluster")
if err != nil {
return err
}
clusterID = resource.ID
}
newProj := &managementClient.Project{
Name: ctx.Args().First(),
ClusterID: clusterID,
Description: ctx.String("description"),
}
_, err = c.ManagementClient.Project.Create(newProj)
if err != nil {
return err
}
return nil
}
func projectDelete(ctx *cli.Context) error {
if ctx.NArg() == 0 {
return cli.ShowSubcommandHelp(ctx)
}
c, err := GetClient(ctx)
if err != nil {
return err
}
for _, arg := range ctx.Args() {
resource, err := Lookup(c, arg, "project")
if err != nil {
return err
}
project, err := getProjectByID(c, resource.ID)
if err != nil {
return err
}
err = c.ManagementClient.Project.Delete(project)
if err != nil {
return err
}
}
return nil
}
func addProjectMemberRoles(ctx *cli.Context) error {
if len(ctx.Args()) < 2 {
return cli.ShowSubcommandHelp(ctx)
}
memberName := ctx.Args().First()
roles := ctx.Args()[1:]
c, err := GetClient(ctx)
if err != nil {
return err
}
member, err := searchForMember(ctx, c, memberName)
if err != nil {
return err
}
projectID := c.UserConfig.Project
if ctx.String("project-id") != "" {
projectID = ctx.String("project-id")
}
for _, role := range roles {
rtb := managementClient.ProjectRoleTemplateBinding{
ProjectID: projectID,
RoleTemplateID: role,
}
if member.PrincipalType == "user" {
rtb.UserPrincipalID = member.ID
} else {
rtb.GroupPrincipalID = member.ID
}
_, err = c.ManagementClient.ProjectRoleTemplateBinding.Create(&rtb)
if err != nil {
return err
}
}
return nil
}
func deleteProjectMemberRoles(ctx *cli.Context) error {
if len(ctx.Args()) < 2 {
return cli.ShowSubcommandHelp(ctx)
}
memberName := ctx.Args().First()
roles := ctx.Args()[1:]
c, err := GetClient(ctx)
if err != nil {
return err
}
member, err := searchForMember(ctx, c, memberName)
if err != nil {
return err
}
projectID := c.UserConfig.Project
if ctx.String("project-id") != "" {
projectID = ctx.String("project-id")
}
for _, role := range roles {
filter := defaultListOpts(ctx)
filter.Filters["projectId"] = projectID
filter.Filters["roleTemplateId"] = role
if member.PrincipalType == "user" {
filter.Filters["userPrincipalId"] = member.ID
} else {
filter.Filters["groupPrincipalId"] = member.ID
}
bindings, err := c.ManagementClient.ProjectRoleTemplateBinding.List(filter)
if err != nil {
return err
}
for _, binding := range bindings.Data {
err = c.ManagementClient.ProjectRoleTemplateBinding.Delete(&binding)
if err != nil {
return err
}
}
}
return nil
}
func listProjectRoles(ctx *cli.Context) error {
return listRoles(ctx, "project")
}
type prtbLister interface {
List(opts *types.ListOpts) (*managementClient.ProjectRoleTemplateBindingCollection, error)
}
func listProjectMembers(ctx *cli.Context, out io.Writer, config userConfig, prtbs prtbLister, principals principalGetter) error {
projectID := config.FocusedProject()
if ctx.String("project-id") != "" {
projectID = ctx.String("project-id")
}
filter := defaultListOpts(ctx)
filter.Filters["projectId"] = projectID
bindings, err := prtbs.List(filter)
if err != nil {
return err
}
rtbs := make([]RoleTemplateBinding, 0, len(bindings.Data))
for _, binding := range bindings.Data {
parsedTime, err := createdTimetoHuman(binding.Created)
if err != nil {
return err
}
principalID := binding.UserPrincipalID
if binding.GroupPrincipalID != "" {
principalID = binding.GroupPrincipalID
}
rtbs = append(rtbs, RoleTemplateBinding{
ID: binding.ID,
Member: getMemberNameFromPrincipal(principals, principalID),
Role: binding.RoleTemplateID,
Created: parsedTime,
})
}
writerConfig := &TableWriterConfig{
Format: ctx.String("format"),
Quiet: ctx.Bool("quiet"),
Writer: out,
}
return listRoleTemplateBindings(writerConfig, rtbs)
}
func getProjectList(
ctx *cli.Context,
c *cliclient.MasterClient,
) (*managementClient.ProjectCollection, error) {
filter := defaultListOpts(ctx)
filter.Filters["clusterId"] = c.UserConfig.FocusedCluster()
collection, err := c.ManagementClient.Project.List(filter)
if err != nil {
return nil, err
}
return collection, nil
}
func getProjectByID(
c *cliclient.MasterClient,
projectID string,
) (*managementClient.Project, error) {
project, err := c.ManagementClient.Project.ByID(projectID)
if err != nil {
return nil, fmt.Errorf("no project found with the ID [%s], run "+
"`rancher projects` to see available projects: %s", projectID, err)
}
return project, nil
}