github.com/argoproj/argo-cd@v1.8.7/cmd/argocd-util/commands/projects.go (about) 1 package commands 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1" 11 appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned" 12 appclient "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/typed/application/v1alpha1" 13 "github.com/argoproj/argo-cd/util/cli" 14 "github.com/argoproj/argo-cd/util/errors" 15 16 "github.com/argoproj/gitops-engine/pkg/utils/kube" 17 "github.com/spf13/cobra" 18 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 19 "k8s.io/client-go/tools/clientcmd" 20 ) 21 22 func NewProjectsCommand() *cobra.Command { 23 var command = &cobra.Command{ 24 Use: "projects", 25 Short: "Utility commands operate on ArgoCD Projects", 26 Run: func(c *cobra.Command, args []string) { 27 c.HelpFunc()(c, args) 28 }, 29 } 30 31 command.AddCommand(NewUpdatePolicyRuleCommand()) 32 command.AddCommand(NewProjectAllowListGenCommand()) 33 return command 34 } 35 36 func globMatch(pattern string, val string) bool { 37 if pattern == "*" { 38 return true 39 } 40 if ok, err := filepath.Match(pattern, val); ok && err == nil { 41 return true 42 } 43 return false 44 } 45 46 func getModification(modification string, resource string, scope string, permission string) (func(string, string) string, error) { 47 switch modification { 48 case "set": 49 if scope == "" { 50 return nil, fmt.Errorf("Flag --group cannot be empty if permission should be set in role") 51 } 52 if permission == "" { 53 return nil, fmt.Errorf("Flag --permission cannot be empty if permission should be set in role") 54 } 55 return func(proj string, action string) string { 56 return fmt.Sprintf("%s, %s, %s/%s, %s", resource, action, proj, scope, permission) 57 }, nil 58 case "remove": 59 return func(proj string, action string) string { 60 return "" 61 }, nil 62 } 63 return nil, fmt.Errorf("modification %s is not supported", modification) 64 } 65 66 func saveProject(updated v1alpha1.AppProject, orig v1alpha1.AppProject, projectsIf appclient.AppProjectInterface, dryRun bool) error { 67 fmt.Printf("===== %s ======\n", updated.Name) 68 target, err := kube.ToUnstructured(&updated) 69 errors.CheckError(err) 70 live, err := kube.ToUnstructured(&orig) 71 if err != nil { 72 return err 73 } 74 _ = cli.PrintDiff(updated.Name, target, live) 75 if !dryRun { 76 _, err = projectsIf.Update(context.Background(), &updated, v1.UpdateOptions{}) 77 if err != nil { 78 return err 79 } 80 } 81 return nil 82 } 83 84 func formatPolicy(proj string, role string, permission string) string { 85 return fmt.Sprintf("p, proj:%s:%s, %s", proj, role, permission) 86 } 87 88 func split(input string, delimiter string) []string { 89 parts := strings.Split(input, delimiter) 90 for i := range parts { 91 parts[i] = strings.TrimSpace(parts[i]) 92 } 93 return parts 94 } 95 96 func NewUpdatePolicyRuleCommand() *cobra.Command { 97 var ( 98 clientConfig clientcmd.ClientConfig 99 resource string 100 scope string 101 rolePattern string 102 permission string 103 dryRun bool 104 ) 105 var command = &cobra.Command{ 106 Use: "update-role-policy PROJECT_GLOB MODIFICATION ACTION", 107 Short: "Implement bulk project role update. Useful to back-fill existing project policies or remove obsolete actions.", 108 Example: ` # Add policy that allows executing any action (action/*) to roles which name matches to *deployer* in all projects 109 argocd-util projects update-role-policy '*' set 'action/*' --role '*deployer*' --resource applications --scope '*' --permission allow 110 111 # Remove policy that which manages running (action/*) from all roles which name matches *deployer* in all projects 112 argocd-util projects update-role-policy '*' remove override --role '*deployer*' 113 `, 114 Run: func(c *cobra.Command, args []string) { 115 if len(args) != 3 { 116 c.HelpFunc()(c, args) 117 os.Exit(1) 118 } 119 projectGlob := args[0] 120 modificationType := args[1] 121 action := args[2] 122 123 config, err := clientConfig.ClientConfig() 124 errors.CheckError(err) 125 config.QPS = 100 126 config.Burst = 50 127 128 namespace, _, err := clientConfig.Namespace() 129 errors.CheckError(err) 130 appclients := appclientset.NewForConfigOrDie(config) 131 132 modification, err := getModification(modificationType, resource, scope, permission) 133 errors.CheckError(err) 134 projIf := appclients.ArgoprojV1alpha1().AppProjects(namespace) 135 136 err = updateProjects(projIf, projectGlob, rolePattern, action, modification, dryRun) 137 errors.CheckError(err) 138 }, 139 } 140 command.Flags().StringVar(&resource, "resource", "", "Resource e.g. 'applications'") 141 command.Flags().StringVar(&scope, "scope", "", "Resource scope e.g. '*'") 142 command.Flags().StringVar(&rolePattern, "role", "*", "Role name pattern e.g. '*deployer*'") 143 command.Flags().StringVar(&permission, "permission", "", "Action permission") 144 command.Flags().BoolVar(&dryRun, "dry-run", true, "Dry run") 145 clientConfig = cli.AddKubectlFlagsToCmd(command) 146 return command 147 } 148 149 func updateProjects(projIf appclient.AppProjectInterface, projectGlob string, rolePattern string, action string, modification func(string, string) string, dryRun bool) error { 150 projects, err := projIf.List(context.Background(), v1.ListOptions{}) 151 if err != nil { 152 return err 153 } 154 for _, proj := range projects.Items { 155 if !globMatch(projectGlob, proj.Name) { 156 continue 157 } 158 origProj := proj.DeepCopy() 159 updated := false 160 for i, role := range proj.Spec.Roles { 161 if !globMatch(rolePattern, role.Name) { 162 continue 163 } 164 actionPolicyIndex := -1 165 for i := range role.Policies { 166 parts := split(role.Policies[i], ",") 167 if len(parts) != 6 || parts[3] != action { 168 continue 169 } 170 actionPolicyIndex = i 171 break 172 } 173 policyPermission := modification(proj.Name, action) 174 if actionPolicyIndex == -1 && policyPermission != "" { 175 updated = true 176 role.Policies = append(role.Policies, formatPolicy(proj.Name, role.Name, policyPermission)) 177 } else if actionPolicyIndex > -1 && policyPermission == "" { 178 updated = true 179 role.Policies = append(role.Policies[:actionPolicyIndex], role.Policies[actionPolicyIndex+1:]...) 180 } else if actionPolicyIndex > -1 && policyPermission != "" { 181 updated = true 182 role.Policies[actionPolicyIndex] = formatPolicy(proj.Name, role.Name, policyPermission) 183 } 184 proj.Spec.Roles[i] = role 185 } 186 if updated { 187 err = saveProject(proj, *origProj, projIf, dryRun) 188 if err != nil { 189 return err 190 } 191 } 192 } 193 return nil 194 }