github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/internal/util/update/fastforward.go (about) 1 // Copyright 2019 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 update 16 17 import ( 18 "fmt" 19 "os" 20 "path/filepath" 21 22 "github.com/GoogleContainerTools/kpt/internal/errors" 23 "github.com/GoogleContainerTools/kpt/internal/pkg" 24 "github.com/GoogleContainerTools/kpt/internal/types" 25 pkgdiff "github.com/GoogleContainerTools/kpt/internal/util/diff" 26 "github.com/GoogleContainerTools/kpt/internal/util/pkgutil" 27 kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1" 28 "github.com/GoogleContainerTools/kpt/pkg/kptfile/kptfileutil" 29 "sigs.k8s.io/kustomize/kyaml/filesys" 30 "sigs.k8s.io/kustomize/kyaml/sets" 31 ) 32 33 // Updater updates a package to a new upstream version. 34 // 35 // If the package at pkgPath differs from the upstream ref it was fetch from, then Update will 36 // fail without making any changes. 37 type FastForwardUpdater struct{} 38 39 var kptfileSet = func() sets.String { 40 s := sets.String{} 41 s.Insert(kptfilev1.KptFileName) 42 return s 43 }() 44 45 // We should try to pull the common code up into the Update command. 46 func (u FastForwardUpdater) Update(options Options) error { 47 const op errors.Op = "update.Update" 48 // Verify that there are no local changes that would prevent us from 49 // using the FastForward strategy. 50 if err := u.checkForLocalChanges(options.LocalPath, options.OriginPath); err != nil { 51 return errors.E(op, types.UniquePath(options.LocalPath), err) 52 } 53 54 if err := (&ReplaceUpdater{}).Update(options); err != nil { 55 return errors.E(op, types.UniquePath(options.LocalPath), err) 56 } 57 return nil 58 } 59 60 func (u FastForwardUpdater) checkForLocalChanges(localPath, originalPath string) error { 61 const op errors.Op = "update.checkForLocalChanges" 62 found, err := pkgutil.Exists(originalPath) 63 if err != nil { 64 return errors.E(op, types.UniquePath(localPath), err) 65 } 66 if !found { 67 return nil 68 } 69 70 subPkgPaths, err := pkgutil.FindSubpackagesForPaths(pkg.Local, true, localPath, originalPath) 71 if err != nil { 72 return errors.E(op, types.UniquePath(localPath), err) 73 } 74 aggDiff := sets.String{} 75 for _, subPkgPath := range append([]string{"."}, subPkgPaths...) { 76 localSubPkgPath := filepath.Join(localPath, subPkgPath) 77 originalSubPkgPath := filepath.Join(originalPath, subPkgPath) 78 79 localExists, err := pkgutil.Exists(localSubPkgPath) 80 if err != nil { 81 return errors.E(op, types.UniquePath(localSubPkgPath), err) 82 } 83 originalExists, err := pkgutil.Exists(originalSubPkgPath) 84 if err != nil { 85 return errors.E(op, types.UniquePath(localSubPkgPath), err) 86 } 87 if !originalExists || !localExists { 88 aggDiff.Insert("%s (Package)", subPkgPath) 89 continue 90 } 91 d, err := pkgdiff.PkgDiff(localSubPkgPath, originalSubPkgPath) 92 if err != nil { 93 return errors.E(op, types.UniquePath(localSubPkgPath), err) 94 } 95 // If the original package didn't have a Kptfile, one was created 96 // in local, but we don't consider that a change unless the user 97 // has made additional changes. 98 if d.Has(kptfilev1.KptFileName) && subPkgPath == "." { 99 hasDiff, err := hasKfDiff(localSubPkgPath, originalSubPkgPath) 100 if err != nil { 101 return errors.E(op, types.UniquePath(localSubPkgPath), err) 102 } 103 if !hasDiff { 104 d = d.Difference(kptfileSet) 105 } 106 } 107 108 aggDiff.Insert(d.List()...) 109 } 110 if aggDiff.Len() > 0 { 111 return errors.E(op, types.UniquePath(localPath), fmt.Sprintf( 112 "local package files have been modified: %v.\n use a different update --strategy.", 113 aggDiff.List())) 114 } 115 return nil 116 } 117 118 func hasKfDiff(localPath, orgPath string) (bool, error) { 119 const op errors.Op = "update.hasKfDiff" 120 localKf, err := pkg.ReadKptfile(filesys.FileSystemOrOnDisk{}, localPath) 121 if err != nil { 122 return false, errors.E(op, types.UniquePath(localPath), err) 123 } 124 localKf.Upstream = nil 125 localKf.UpstreamLock = nil 126 127 _, err = os.Stat(filepath.Join(orgPath, kptfilev1.KptFileName)) 128 if err != nil { 129 if os.IsNotExist(err) { 130 // We know that there aren't any Kptfile in the original 131 // package, so we ignore the diff if the local Kptfile 132 // is just the minimal Kptfile generated automatically. 133 isDefault, err := isDefaultKptfile(localKf, filepath.Base(localPath)) 134 if err != nil { 135 return false, errors.E(op, types.UniquePath(localPath), err) 136 } 137 return !isDefault, nil 138 } 139 return false, errors.E(op, types.UniquePath(localPath), err) 140 } 141 orgKf, err := pkg.ReadKptfile(filesys.FileSystemOrOnDisk{}, orgPath) 142 if err != nil { 143 return false, errors.E(op, types.UniquePath(localPath), err) 144 } 145 146 orgKf.Name = localKf.Name 147 equal, err := kptfileutil.Equal(localKf, orgKf) 148 if err != nil { 149 return false, errors.E(op, types.UniquePath(localPath), err) 150 } 151 152 return !equal, nil 153 } 154 155 func isDefaultKptfile(localKf *kptfilev1.KptFile, name string) (bool, error) { 156 defaultKf := kptfileutil.DefaultKptfile(name) 157 return kptfileutil.Equal(localKf, defaultKf) 158 }