github.com/opendevstack/tailor@v1.3.5-0.20220119161809-cab064e60a67/pkg/commands/apply.go (about) 1 package commands 2 3 import ( 4 "bufio" 5 "bytes" 6 "errors" 7 "fmt" 8 "io" 9 10 "github.com/opendevstack/tailor/pkg/cli" 11 "github.com/opendevstack/tailor/pkg/openshift" 12 ) 13 14 type printChange func(w io.Writer, change *openshift.Change, revealSecrets bool) 15 type handleChange func(label string, change *openshift.Change, compareOptions *cli.CompareOptions, ocClient cli.ClientModifier) error 16 17 // Apply prints the drift between desired and current state to STDOUT. 18 // If there is any, it asks for confirmation and applies the changeset. 19 func Apply(nonInteractive bool, compareOptions *cli.CompareOptions, ocClient cli.ClientApplier, stdin io.Reader) (bool, error) { 20 stdinReader := bufio.NewReader(stdin) 21 22 var buf bytes.Buffer 23 driftDetected, changeset, err := calculateChangeset(&buf, compareOptions, ocClient) 24 fmt.Print(buf.String()) 25 if err != nil { 26 return driftDetected, err 27 } 28 29 if driftDetected { 30 if nonInteractive { 31 err = apply(compareOptions, changeset, ocClient) 32 if err != nil { 33 return true, fmt.Errorf("Apply aborted: %s", err) 34 } 35 if compareOptions.Verify { 36 err := performVerification(compareOptions, ocClient) 37 if err != nil { 38 return true, err 39 } 40 } 41 // As apply has run successfully, there should not be any drift 42 // anymore. Therefore we report no drift here. 43 return false, nil 44 } 45 46 options := []string{"y=yes", "n=no"} 47 // Selecting makes no sense when --verify is given, as the verification 48 // would fail if not all changes are selected. 49 // Selecting is also pointless if there is only one change in total. 50 allowSelecting := !compareOptions.Verify && !changeset.ExactlyOne() 51 if allowSelecting { 52 options = append(options, "s=select") 53 } 54 a := cli.AskForAction("Apply all changes?", options, stdinReader) 55 if a == "y" { 56 fmt.Println("") 57 err = apply(compareOptions, changeset, ocClient) 58 if err != nil { 59 return true, fmt.Errorf("Apply aborted: %s", err) 60 } 61 if compareOptions.Verify { 62 err := performVerification(compareOptions, ocClient) 63 if err != nil { 64 return true, err 65 } 66 } 67 // As apply has run successfully, there should not be any drift 68 // anymore. Therefore we report no drift here. 69 return false, nil 70 } else if allowSelecting && a == "s" { 71 anyChangeSkipped := false 72 73 anyDeleteChangeSkipped, err := askAndApply(compareOptions, ocClient, stdinReader, changeset.Delete, printDeleteChange, "Deleting", ocDelete) 74 if err != nil { 75 return true, fmt.Errorf("Apply aborted: %s", err) 76 } else if anyDeleteChangeSkipped { 77 anyChangeSkipped = true 78 } 79 anyCreateChangeSkipped, err := askAndApply(compareOptions, ocClient, stdinReader, changeset.Create, printCreateChange, "Creating", ocApply) 80 if err != nil { 81 return true, fmt.Errorf("Apply aborted: %s", err) 82 } else if anyCreateChangeSkipped { 83 anyChangeSkipped = true 84 } 85 anyUpdateChangeSkipped, err := askAndApply(compareOptions, ocClient, stdinReader, changeset.Update, printUpdateChange, "Updating", ocApply) 86 if err != nil { 87 return true, fmt.Errorf("Apply aborted: %s", err) 88 } else if anyUpdateChangeSkipped { 89 anyChangeSkipped = true 90 } 91 92 return anyChangeSkipped, nil 93 } 94 95 // Changes were not applied, so we report that drift was detected. 96 return true, nil 97 } 98 99 // No drift, nothing to do ... 100 return false, nil 101 } 102 103 func askAndApply(compareOptions *cli.CompareOptions, ocClient cli.ClientApplier, stdinReader *bufio.Reader, changes []*openshift.Change, changePrinter printChange, label string, changeHandler handleChange) (bool, error) { 104 anyChangeSkipped := false 105 106 for _, change := range changes { 107 fmt.Println("") 108 var buf bytes.Buffer 109 changePrinter(&buf, change, compareOptions.RevealSecrets) 110 fmt.Print(buf.String()) 111 a := cli.AskForAction( 112 fmt.Sprintf("Apply change to %s?", change.ItemName()), 113 []string{"y=yes", "n=no"}, 114 stdinReader, 115 ) 116 if a == "y" { 117 fmt.Println("") 118 err := changeHandler(label, change, compareOptions, ocClient) 119 if err != nil { 120 return true, fmt.Errorf("Apply aborted: %s", err) 121 } 122 } else { 123 anyChangeSkipped = true 124 } 125 } 126 return anyChangeSkipped, nil 127 } 128 129 func apply(compareOptions *cli.CompareOptions, c *openshift.Changeset, ocClient cli.ClientModifier) error { 130 131 for _, change := range c.Delete { 132 err := ocDelete("Deleting", change, compareOptions, ocClient) 133 if err != nil { 134 return err 135 } 136 } 137 138 for _, change := range c.Create { 139 err := ocApply("Creating", change, compareOptions, ocClient) 140 if err != nil { 141 return err 142 } 143 } 144 145 for _, change := range c.Update { 146 err := ocApply("Updating", change, compareOptions, ocClient) 147 if err != nil { 148 return err 149 } 150 } 151 152 return nil 153 } 154 155 func ocDelete(label string, change *openshift.Change, compareOptions *cli.CompareOptions, ocClient cli.ClientModifier) error { 156 fmt.Printf("%s %s ... ", label, change.ItemName()) 157 errBytes, err := ocClient.Delete(change.Kind, change.Name) 158 if err == nil { 159 fmt.Println("done") 160 } else { 161 fmt.Println("failed") 162 return errors.New(string(errBytes)) 163 } 164 return nil 165 } 166 167 func ocApply(label string, change *openshift.Change, compareOptions *cli.CompareOptions, ocClient cli.ClientModifier) error { 168 fmt.Printf("%s %s ... ", label, change.ItemName()) 169 errBytes, err := ocClient.Apply(change.DesiredState, compareOptions.Selector) 170 if err == nil { 171 fmt.Println("done") 172 } else { 173 fmt.Println("failed") 174 return errors.New(string(errBytes)) 175 } 176 177 return nil 178 } 179 180 func performVerification(compareOptions *cli.CompareOptions, ocClient cli.ClientProcessorExporter) error { 181 var buf bytes.Buffer 182 fmt.Print("\nVerifying current state matches desired state ... ") 183 driftDetected, _, err := calculateChangeset(&buf, compareOptions, ocClient) 184 if err != nil { 185 return fmt.Errorf("Error: %s", err) 186 } 187 if driftDetected { 188 fmt.Print("failed! Detected drift:\n\n") 189 fmt.Println(buf.String()) 190 return errors.New("Verification failed") 191 } 192 fmt.Println("successful") 193 return nil 194 }