github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/commands/alpha/sync/create/command.go (about) 1 // Copyright 2022 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package create 16 17 import ( 18 "context" 19 "fmt" 20 21 "github.com/GoogleContainerTools/kpt/commands/util" 22 "github.com/GoogleContainerTools/kpt/internal/docs/generated/syncdocs" 23 "github.com/GoogleContainerTools/kpt/internal/errors" 24 "github.com/GoogleContainerTools/kpt/internal/util/porch" 25 porchapi "github.com/GoogleContainerTools/kpt/porch/api/porch/v1alpha1" 26 configapi "github.com/GoogleContainerTools/kpt/porch/api/porchconfig/v1alpha1" 27 "github.com/spf13/cobra" 28 coreapi "k8s.io/api/core/v1" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 31 "k8s.io/cli-runtime/pkg/genericclioptions" 32 "sigs.k8s.io/controller-runtime/pkg/client" 33 ) 34 35 const ( 36 command = "cmdsync.create" 37 ) 38 39 func newRunner(ctx context.Context, rcg *genericclioptions.ConfigFlags) *runner { 40 r := &runner{ 41 ctx: ctx, 42 cfg: rcg, 43 } 44 c := &cobra.Command{ 45 Use: "create NAME", 46 Short: syncdocs.CreateShort, 47 Long: syncdocs.CreateShort + "\n" + syncdocs.CreateLong, 48 Example: syncdocs.CreateExamples, 49 PreRunE: r.preRunE, 50 RunE: r.runE, 51 Hidden: porch.HidePorchCommands, 52 } 53 r.Command = c 54 55 c.Flags().StringVar(&r.syncPkg, "package", "", "Name of the package revision to sync. Required.") 56 57 return r 58 } 59 60 func NewCommand(ctx context.Context, rcg *genericclioptions.ConfigFlags) *cobra.Command { 61 return newRunner(ctx, rcg).Command 62 } 63 64 type runner struct { 65 ctx context.Context 66 cfg *genericclioptions.ConfigFlags 67 client client.Client 68 Command *cobra.Command 69 70 // Flags 71 syncPkg string 72 } 73 74 func (r *runner) preRunE(cmd *cobra.Command, args []string) error { 75 const op errors.Op = command + ".preRunE" 76 77 if len(args) == 0 { 78 return errors.E(op, "NAME is required positional argument") 79 } 80 if r.syncPkg == "" { 81 return errors.E(op, "--package is a required flag") 82 } 83 84 client, err := porch.CreateDynamicClient(r.cfg) 85 if err != nil { 86 return errors.E(op, err) 87 } 88 89 r.client = client 90 return nil 91 } 92 93 func (r *runner) runE(cmd *cobra.Command, args []string) error { 94 const op errors.Op = command + ".runE" 95 96 syncName := args[0] 97 98 var pr porchapi.PackageRevision 99 if err := r.client.Get(r.ctx, client.ObjectKey{ 100 Namespace: *r.cfg.Namespace, 101 Name: r.syncPkg, 102 }, &pr); err != nil { 103 return errors.E(op, err) 104 } 105 106 var repository configapi.Repository 107 if err := r.client.Get(r.ctx, client.ObjectKey{ 108 Namespace: *r.cfg.Namespace, 109 Name: pr.Spec.RepositoryName, 110 }, &repository); err != nil { 111 return errors.E(op, err) 112 } 113 114 if repository.Spec.Type != configapi.RepositoryTypeGit { 115 return errors.E(op, fmt.Sprintf("repository %s/%s is not a git repository; %s is not supported", 116 repository.Namespace, repository.Name, repository.Spec.Type)) 117 } 118 if repository.Spec.Git == nil { 119 return errors.E(op, fmt.Sprintf("repository %s/%s is missing Git spec", repository.Namespace, repository.Name)) 120 } 121 122 var secret coreapi.Secret 123 124 if secretName := repository.Spec.Git.SecretRef.Name; secretName != "" { 125 var repositorySecret coreapi.Secret 126 key := client.ObjectKey{Namespace: *r.cfg.Namespace, Name: repository.Spec.Git.SecretRef.Name} 127 if err := r.client.Get(r.ctx, key, &repositorySecret); err != nil { 128 return errors.E(op, fmt.Sprintf("cannot retrieve repository credentials %s: %v", key, err)) 129 } 130 131 secret = coreapi.Secret{ 132 TypeMeta: metav1.TypeMeta{ 133 Kind: "Secret", 134 APIVersion: coreapi.SchemeGroupVersion.Identifier(), 135 }, 136 ObjectMeta: metav1.ObjectMeta{ 137 Name: fmt.Sprintf("%s-auth", syncName), 138 Namespace: util.RootSyncNamespace, 139 }, 140 Data: map[string][]byte{ 141 "username": repositorySecret.Data["username"], 142 "token": repositorySecret.Data["password"], 143 }, 144 } 145 146 if err := porch.Apply(r.ctx, r.client, &secret); err != nil { 147 return errors.E(op, err) 148 } 149 } 150 151 git := map[string]interface{}{ 152 "repo": repository.Spec.Git.Repo, 153 "revision": fmt.Sprintf("%s/%s", pr.Spec.PackageName, pr.Spec.Revision), 154 "dir": pr.Spec.PackageName, 155 "branch": repository.Spec.Git.Branch, 156 } 157 158 if secret.Name != "" { 159 git["auth"] = "token" 160 git["secretRef"] = map[string]interface{}{ 161 "name": secret.Name, 162 } 163 } 164 165 rootsync := &unstructured.Unstructured{ 166 Object: map[string]interface{}{ 167 "apiVersion": "configsync.gke.io/v1beta1", 168 "kind": "RootSync", 169 "metadata": map[string]interface{}{ 170 "name": syncName, 171 "namespace": util.RootSyncNamespace, 172 }, 173 "spec": map[string]interface{}{ 174 "sourceFormat": "unstructured", 175 "git": git, 176 }, 177 }, 178 } 179 180 fmt.Println(rootsync.GetName()) 181 fmt.Println(rootsync.GetNamespace()) 182 183 if err := porch.Apply(r.ctx, r.client, rootsync); err != nil { 184 return errors.E(op, err) 185 } 186 187 fmt.Fprintf(r.Command.OutOrStderr(), "Created RootSync config-management-system/%s", syncName) 188 return nil 189 }