github.com/cloudfoundry-attic/ltc@v0.0.0-20151123212628-098adc7919fc/config/command_factory/config_command_factory_test.go (about) 1 package command_factory_test 2 3 import ( 4 "errors" 5 "io" 6 7 . "github.com/onsi/ginkgo" 8 . "github.com/onsi/gomega" 9 "github.com/onsi/gomega/gbytes" 10 11 "github.com/cloudfoundry-incubator/ltc/config/command_factory" 12 "github.com/cloudfoundry-incubator/ltc/config/command_factory/fake_blob_store_verifier" 13 "github.com/cloudfoundry-incubator/ltc/config/persister" 14 "github.com/cloudfoundry-incubator/ltc/config/target_verifier/fake_target_verifier" 15 "github.com/cloudfoundry-incubator/ltc/exit_handler/exit_codes" 16 "github.com/cloudfoundry-incubator/ltc/exit_handler/fake_exit_handler" 17 "github.com/cloudfoundry-incubator/ltc/terminal" 18 "github.com/cloudfoundry-incubator/ltc/terminal/mocks" 19 "github.com/cloudfoundry-incubator/ltc/test_helpers" 20 "github.com/cloudfoundry-incubator/ltc/version/fake_version_manager" 21 "github.com/codegangsta/cli" 22 23 config_package "github.com/cloudfoundry-incubator/ltc/config" 24 ) 25 26 var _ = Describe("CommandFactory", func() { 27 var ( 28 stdinReader *io.PipeReader 29 stdinWriter *io.PipeWriter 30 outputBuffer *gbytes.Buffer 31 terminalUI terminal.UI 32 config *config_package.Config 33 configPersister persister.Persister 34 fakeTargetVerifier *fake_target_verifier.FakeTargetVerifier 35 fakeBlobStoreVerifier *fake_blob_store_verifier.FakeBlobStoreVerifier 36 fakeExitHandler *fake_exit_handler.FakeExitHandler 37 fakePasswordReader *mocks.FakePasswordReader 38 fakeVersionManager *fake_version_manager.FakeVersionManager 39 ) 40 41 BeforeEach(func() { 42 stdinReader, stdinWriter = io.Pipe() 43 outputBuffer = gbytes.NewBuffer() 44 fakeExitHandler = &fake_exit_handler.FakeExitHandler{} 45 fakePasswordReader = &mocks.FakePasswordReader{} 46 terminalUI = terminal.NewUI(stdinReader, outputBuffer, fakePasswordReader) 47 fakeTargetVerifier = &fake_target_verifier.FakeTargetVerifier{} 48 fakeBlobStoreVerifier = &fake_blob_store_verifier.FakeBlobStoreVerifier{} 49 fakeVersionManager = &fake_version_manager.FakeVersionManager{} 50 configPersister = persister.NewMemPersister() 51 config = config_package.New(configPersister) 52 }) 53 54 Describe("TargetCommand", func() { 55 var targetCommand cli.Command 56 57 verifyOldTargetStillSet := func() { 58 newConfig := config_package.New(configPersister) 59 Expect(newConfig.Load()).To(Succeed()) 60 61 Expect(newConfig.Receptor()).To(Equal("http://olduser:oldpass@receptor.oldtarget.com")) 62 } 63 64 BeforeEach(func() { 65 commandFactory := command_factory.NewConfigCommandFactory(config, terminalUI, fakeTargetVerifier, fakeBlobStoreVerifier, fakeExitHandler, fakeVersionManager) 66 targetCommand = commandFactory.MakeTargetCommand() 67 68 config.SetTarget("oldtarget.com") 69 config.SetLogin("olduser", "oldpass") 70 Expect(config.Save()).To(Succeed()) 71 }) 72 73 Context("displaying the target", func() { 74 JustBeforeEach(func() { 75 test_helpers.ExecuteCommandWithArgs(targetCommand, []string{}) 76 }) 77 78 It("outputs the current user and target host", func() { 79 Expect(outputBuffer).To(test_helpers.SayLine("Target:\t\tolduser@oldtarget.com")) 80 }) 81 82 Context("when no username is set", func() { 83 BeforeEach(func() { 84 config.SetLogin("", "") 85 Expect(config.Save()).To(Succeed()) 86 }) 87 88 It("only prints the target", func() { 89 Expect(outputBuffer).To(test_helpers.SayLine("Target:\t\toldtarget.com")) 90 }) 91 }) 92 93 Context("when no target is set", func() { 94 BeforeEach(func() { 95 config.SetTarget("") 96 Expect(config.Save()).To(Succeed()) 97 }) 98 99 It("informs the user the target is not set", func() { 100 Expect(outputBuffer).To(test_helpers.SayLine("Target not set.")) 101 }) 102 }) 103 104 Context("when no blob store is targeted", func() { 105 It("should specify that no blob store is targeted", func() { 106 Expect(outputBuffer).To(test_helpers.SayLine("\tNo droplet store specified.")) 107 }) 108 }) 109 110 Context("when a DAV blob store is targeted", func() { 111 BeforeEach(func() { 112 config.SetBlobStore("blobtarget.com", "8444", "blobUser", "password") 113 Expect(config.Save()).To(Succeed()) 114 }) 115 116 It("outputs the current user and blob store host", func() { 117 Expect(outputBuffer).To(test_helpers.SayLine("Droplet store:\tblobUser@blobtarget.com:8444")) 118 }) 119 120 Context("when no blob store username is set", func() { 121 BeforeEach(func() { 122 config.SetBlobStore("blobtarget.com", "8444", "", "") 123 Expect(config.Save()).To(Succeed()) 124 }) 125 126 It("only prints the blob store host", func() { 127 Expect(outputBuffer).To(test_helpers.SayLine("Droplet store:\tblobtarget.com:8444")) 128 }) 129 }) 130 }) 131 132 Context("when a S3 blob store is targeted", func() { 133 BeforeEach(func() { 134 config.SetS3BlobStore("access", "secret", "bucket", "region") 135 Expect(config.Save()).To(Succeed()) 136 }) 137 138 It("outputs the s3 bucket and region", func() { 139 Expect(outputBuffer).To(test_helpers.SayLine("Droplet store:\ts3://bucket (region)")) 140 }) 141 }) 142 }) 143 144 Context("when --domain is pased", func() { 145 JustBeforeEach(func() { 146 test_helpers.ExecuteCommandWithArgs(targetCommand, []string{"--domain"}) 147 }) 148 149 It("outputs just the target host", func() { 150 Expect(string(outputBuffer.Contents())).To(Equal("oldtarget.com\n")) 151 }) 152 153 Context("when no target is set", func() { 154 BeforeEach(func() { 155 config.SetTarget("") 156 Expect(config.Save()).To(Succeed()) 157 }) 158 159 It("informs the user the target is not set", func() { 160 Expect(string(outputBuffer.Contents())).To(BeEmpty()) 161 }) 162 }) 163 }) 164 165 Context("when initially connecting to the receptor without authentication", func() { 166 BeforeEach(func() { 167 fakeTargetVerifier.VerifyTargetReturns(true, true, nil) 168 fakeBlobStoreVerifier.VerifyReturns(true, nil) 169 }) 170 171 It("saves the new receptor target", func() { 172 test_helpers.ExecuteCommandWithArgs(targetCommand, []string{"myapi.com"}) 173 174 Expect(fakeTargetVerifier.VerifyTargetCallCount()).To(Equal(1)) 175 Expect(fakeTargetVerifier.VerifyTargetArgsForCall(0)).To(Equal("http://receptor.myapi.com")) 176 177 newConfig := config_package.New(configPersister) 178 Expect(newConfig.Load()).To(Succeed()) 179 Expect(newConfig.Receptor()).To(Equal("http://receptor.myapi.com")) 180 }) 181 182 It("clears out existing saved target credentials", func() { 183 test_helpers.ExecuteCommandWithArgs(targetCommand, []string{"myapi.com"}) 184 185 Expect(fakeTargetVerifier.VerifyTargetCallCount()).To(Equal(1)) 186 Expect(fakeTargetVerifier.VerifyTargetArgsForCall(0)).To(Equal("http://receptor.myapi.com")) 187 }) 188 189 It("saves the new blob store target", func() { 190 fakeBlobStoreVerifier.VerifyReturns(true, nil) 191 192 test_helpers.ExecuteCommandWithArgs(targetCommand, []string{"myapi.com"}) 193 194 Expect(fakeBlobStoreVerifier.VerifyCallCount()).To(Equal(1)) 195 196 config := fakeBlobStoreVerifier.VerifyArgsForCall(0) 197 blobStoreConfig := config.BlobStore() 198 Expect(blobStoreConfig).To(Equal(config_package.BlobStoreConfig{ 199 Host: "myapi.com", 200 Port: "8444", 201 })) 202 203 newConfig := config_package.New(configPersister) 204 Expect(newConfig.Load()).To(Succeed()) 205 Expect(newConfig.BlobStore()).To(Equal(config_package.BlobStoreConfig{ 206 Host: "myapi.com", 207 Port: "8444", 208 })) 209 }) 210 211 Context("when the blob store requires authorization", func() { 212 It("exits", func() { 213 fakeBlobStoreVerifier.VerifyReturns(false, nil) 214 215 test_helpers.ExecuteCommandWithArgs(targetCommand, []string{"myapi.com"}) 216 217 Expect(fakeBlobStoreVerifier.VerifyCallCount()).To(Equal(1)) 218 219 config := fakeBlobStoreVerifier.VerifyArgsForCall(0) 220 blobStoreConfig := config.BlobStore() 221 Expect(blobStoreConfig).To(Equal(config_package.BlobStoreConfig{ 222 Host: "myapi.com", 223 Port: "8444", 224 })) 225 226 Expect(outputBuffer).To(test_helpers.SayLine("Could not authenticate with the droplet store.")) 227 verifyOldTargetStillSet() 228 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.BadTarget})) 229 }) 230 }) 231 232 Context("when the blob store target is offline", func() { 233 It("exits", func() { 234 fakeBlobStoreVerifier.VerifyReturns(false, errors.New("some error")) 235 236 test_helpers.ExecuteCommandWithArgs(targetCommand, []string{"myapi.com"}) 237 238 Expect(fakeBlobStoreVerifier.VerifyCallCount()).To(Equal(1)) 239 240 config := fakeBlobStoreVerifier.VerifyArgsForCall(0) 241 blobStoreConfig := config.BlobStore() 242 Expect(blobStoreConfig).To(Equal(config_package.BlobStoreConfig{ 243 Host: "myapi.com", 244 Port: "8444", 245 })) 246 247 Expect(outputBuffer).To(test_helpers.SayLine("Could not connect to the droplet store.")) 248 verifyOldTargetStillSet() 249 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.BadTarget})) 250 }) 251 }) 252 253 Context("when the persister returns errors", func() { 254 BeforeEach(func() { 255 commandFactory := command_factory.NewConfigCommandFactory(config_package.New(errorPersister("some error")), terminalUI, fakeTargetVerifier, fakeBlobStoreVerifier, fakeExitHandler, fakeVersionManager) 256 targetCommand = commandFactory.MakeTargetCommand() 257 }) 258 259 It("exits", func() { 260 test_helpers.ExecuteCommandWithArgs(targetCommand, []string{"myapi.com"}) 261 262 Eventually(outputBuffer).Should(test_helpers.SayLine("some error")) 263 verifyOldTargetStillSet() 264 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.FileSystemError})) 265 }) 266 }) 267 }) 268 269 Context("when the receptor requires authentication", func() { 270 BeforeEach(func() { 271 fakeTargetVerifier.VerifyTargetReturns(true, false, nil) 272 fakeBlobStoreVerifier.VerifyReturns(true, nil) 273 fakePasswordReader.PromptForPasswordReturns("testpassword") 274 }) 275 276 It("prompts for credentials and stores them in the config", func() { 277 doneChan := test_helpers.AsyncExecuteCommandWithArgs(targetCommand, []string{"myapi.com"}) 278 279 Eventually(outputBuffer).Should(test_helpers.Say("Username: ")) 280 fakeTargetVerifier.VerifyTargetReturns(true, true, nil) 281 stdinWriter.Write([]byte("testusername\n")) 282 283 Eventually(doneChan, 3).Should(BeClosed()) 284 285 Expect(config.Target()).To(Equal("myapi.com")) 286 Expect(config.Receptor()).To(Equal("http://testusername:testpassword@receptor.myapi.com")) 287 Expect(outputBuffer).To(test_helpers.SayLine("API location set.")) 288 289 Expect(fakePasswordReader.PromptForPasswordCallCount()).To(Equal(1)) 290 Expect(fakePasswordReader.PromptForPasswordArgsForCall(0)).To(Equal("Password")) 291 292 Expect(fakeTargetVerifier.VerifyTargetCallCount()).To(Equal(2)) 293 Expect(fakeTargetVerifier.VerifyTargetArgsForCall(0)).To(Equal("http://receptor.myapi.com")) 294 Expect(fakeTargetVerifier.VerifyTargetArgsForCall(1)).To(Equal("http://testusername:testpassword@receptor.myapi.com")) 295 }) 296 297 Context("when provided receptor credentials are invalid", func() { 298 It("does not save the config", func() { 299 fakePasswordReader.PromptForPasswordReturns("some-invalid-password") 300 doneChan := test_helpers.AsyncExecuteCommandWithArgs(targetCommand, []string{"newtarget.com"}) 301 302 Eventually(outputBuffer).Should(test_helpers.Say("Username: ")) 303 stdinWriter.Write([]byte("some-invalid-user\n")) 304 305 Eventually(doneChan, 3).Should(BeClosed()) 306 307 Expect(fakePasswordReader.PromptForPasswordCallCount()).To(Equal(1)) 308 Expect(fakePasswordReader.PromptForPasswordArgsForCall(0)).To(Equal("Password")) 309 310 Expect(outputBuffer).To(test_helpers.SayLine("Could not authorize target.")) 311 312 verifyOldTargetStillSet() 313 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.BadTarget})) 314 }) 315 }) 316 317 Context("when the receptor returns an error while verifying the provided credentials", func() { 318 It("does not save the config", func() { 319 fakePasswordReader.PromptForPasswordReturns("some-invalid-password") 320 doneChan := test_helpers.AsyncExecuteCommandWithArgs(targetCommand, []string{"newtarget.com"}) 321 322 Eventually(outputBuffer).Should(test_helpers.Say("Username: ")) 323 324 fakeTargetVerifier.VerifyTargetReturns(true, false, errors.New("Unknown Error")) 325 stdinWriter.Write([]byte("some-invalid-user\n")) 326 327 Eventually(doneChan, 3).Should(BeClosed()) 328 329 Expect(fakePasswordReader.PromptForPasswordCallCount()).To(Equal(1)) 330 Expect(fakePasswordReader.PromptForPasswordArgsForCall(0)).To(Equal("Password")) 331 332 Expect(outputBuffer).To(test_helpers.SayLine("Error verifying target: Unknown Error")) 333 334 verifyOldTargetStillSet() 335 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.BadTarget})) 336 }) 337 }) 338 339 Context("when the receptor credentials work on the blob store", func() { 340 It("saves the new blob store target", func() { 341 fakeBlobStoreVerifier.VerifyReturns(true, nil) 342 343 doneChan := test_helpers.AsyncExecuteCommandWithArgs(targetCommand, []string{"myapi.com"}) 344 345 Eventually(outputBuffer).Should(test_helpers.Say("Username: ")) 346 fakeTargetVerifier.VerifyTargetReturns(true, true, nil) 347 stdinWriter.Write([]byte("testusername\n")) 348 349 Eventually(doneChan, 3).Should(BeClosed()) 350 351 Expect(fakeBlobStoreVerifier.VerifyCallCount()).To(Equal(1)) 352 353 config := fakeBlobStoreVerifier.VerifyArgsForCall(0) 354 blobStoreConfig := config.BlobStore() 355 Expect(blobStoreConfig).To(Equal(config_package.BlobStoreConfig{ 356 Host: "myapi.com", 357 Port: "8444", 358 Username: "testusername", 359 Password: "testpassword", 360 })) 361 362 newConfig := config_package.New(configPersister) 363 Expect(newConfig.Load()).To(Succeed()) 364 Expect(newConfig.Receptor()).To(Equal("http://testusername:testpassword@receptor.myapi.com")) 365 Expect(newConfig.BlobStore()).To(Equal(config_package.BlobStoreConfig{ 366 Host: "myapi.com", 367 Port: "8444", 368 Username: "testusername", 369 Password: "testpassword", 370 })) 371 }) 372 }) 373 374 Context("when the receptor credentials don't work on the blob store", func() { 375 It("does not save the config", func() { 376 fakeBlobStoreVerifier.VerifyReturns(false, nil) 377 378 doneChan := test_helpers.AsyncExecuteCommandWithArgs(targetCommand, []string{"myapi.com"}) 379 380 Eventually(outputBuffer).Should(test_helpers.Say("Username: ")) 381 fakeTargetVerifier.VerifyTargetReturns(true, true, nil) 382 stdinWriter.Write([]byte("testusername\n")) 383 384 Eventually(doneChan, 3).Should(BeClosed()) 385 386 Expect(fakeBlobStoreVerifier.VerifyCallCount()).To(Equal(1)) 387 388 config := fakeBlobStoreVerifier.VerifyArgsForCall(0) 389 blobStoreConfig := config.BlobStore() 390 Expect(blobStoreConfig).To(Equal(config_package.BlobStoreConfig{ 391 Host: "myapi.com", 392 Port: "8444", 393 Username: "testusername", 394 Password: "testpassword", 395 })) 396 397 Expect(outputBuffer).To(test_helpers.SayLine("Could not authenticate with the droplet store.")) 398 verifyOldTargetStillSet() 399 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.BadTarget})) 400 }) 401 }) 402 403 Context("when the blob store is offline", func() { 404 It("does not save the config", func() { 405 fakeBlobStoreVerifier.VerifyReturns(false, errors.New("some error")) 406 407 doneChan := test_helpers.AsyncExecuteCommandWithArgs(targetCommand, []string{"myapi.com"}) 408 409 Eventually(outputBuffer).Should(test_helpers.Say("Username: ")) 410 fakeTargetVerifier.VerifyTargetReturns(true, true, nil) 411 stdinWriter.Write([]byte("testusername\n")) 412 413 Eventually(doneChan, 3).Should(BeClosed()) 414 415 Expect(fakeBlobStoreVerifier.VerifyCallCount()).To(Equal(1)) 416 417 config := fakeBlobStoreVerifier.VerifyArgsForCall(0) 418 blobStoreConfig := config.BlobStore() 419 Expect(blobStoreConfig).To(Equal(config_package.BlobStoreConfig{ 420 Host: "myapi.com", 421 Port: "8444", 422 Username: "testusername", 423 Password: "testpassword", 424 })) 425 426 Expect(outputBuffer).To(test_helpers.SayLine("Could not connect to the droplet store.")) 427 verifyOldTargetStillSet() 428 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.BadTarget})) 429 }) 430 }) 431 }) 432 433 Context("s3", func() { 434 It("prompts for s3 configuration when --s3 is passed", func() { 435 fakeTargetVerifier.VerifyTargetReturns(true, true, nil) 436 fakeBlobStoreVerifier.VerifyReturns(true, nil) 437 fakePasswordReader.PromptForPasswordReturns("some-secret") 438 439 doneChan := test_helpers.AsyncExecuteCommandWithArgs(targetCommand, []string{"myapi.com", "--s3"}) 440 441 Eventually(outputBuffer).Should(test_helpers.Say("S3 Access Key: ")) 442 stdinWriter.Write([]byte("some-access\n")) 443 Eventually(outputBuffer).Should(test_helpers.Say("S3 Bucket: ")) 444 stdinWriter.Write([]byte("some-bucket\n")) 445 Eventually(outputBuffer).Should(test_helpers.Say("S3 Region: ")) 446 stdinWriter.Write([]byte("some-region\n")) 447 448 Eventually(doneChan, 3).Should(BeClosed()) 449 450 Expect(fakeBlobStoreVerifier.VerifyCallCount()).To(Equal(1)) 451 config := fakeBlobStoreVerifier.VerifyArgsForCall(0) 452 s3BlobTargetInfo := config.S3BlobStore() 453 Expect(s3BlobTargetInfo.AccessKey).To(Equal("some-access")) 454 Expect(s3BlobTargetInfo.SecretKey).To(Equal("some-secret")) 455 Expect(s3BlobTargetInfo.BucketName).To(Equal("some-bucket")) 456 Expect(s3BlobTargetInfo.Region).To(Equal("some-region")) 457 458 newConfig := config_package.New(configPersister) 459 Expect(newConfig.Load()).To(Succeed()) 460 newS3BlobTargetInfo := newConfig.S3BlobStore() 461 Expect(newS3BlobTargetInfo.AccessKey).To(Equal("some-access")) 462 Expect(newS3BlobTargetInfo.SecretKey).To(Equal("some-secret")) 463 Expect(newS3BlobTargetInfo.BucketName).To(Equal("some-bucket")) 464 Expect(newS3BlobTargetInfo.Region).To(Equal("some-region")) 465 }) 466 }) 467 468 Context("setting an invalid target", func() { 469 It("does not save the config", func() { 470 fakeTargetVerifier.VerifyTargetReturns(true, false, errors.New("Unknown Error")) 471 472 test_helpers.ExecuteCommandWithArgs(targetCommand, []string{"newtarget.com"}) 473 474 Expect(outputBuffer).To(test_helpers.SayLine("Error verifying target: Unknown Error")) 475 476 verifyOldTargetStillSet() 477 Expect(fakeExitHandler.ExitCalledWith).To(Equal([]int{exit_codes.BadTarget})) 478 }) 479 }) 480 481 Context("checking ltc target version", func() { 482 BeforeEach(func() { 483 fakeTargetVerifier.VerifyTargetReturns(true, true, nil) 484 fakeBlobStoreVerifier.VerifyReturns(true, nil) 485 fakeVersionManager.LatticeVersionReturns("some-version") 486 }) 487 488 It("should print warning and recommend sync if ltc version does not match server", func() { 489 fakeVersionManager.LtcMatchesServerReturns(false, nil) 490 491 test_helpers.ExecuteCommandWithArgs(targetCommand, []string{"target.com"}) 492 493 Expect(fakeVersionManager.LtcMatchesServerCallCount()).To(Equal(1)) 494 Expect(fakeVersionManager.LtcMatchesServerArgsForCall(0)).To(Equal("http://receptor.target.com")) 495 496 Expect(outputBuffer).To(test_helpers.SayLine("WARNING: local ltc version (some-version) does not match target expected version.")) 497 Expect(outputBuffer).To(test_helpers.SayLine("Run `ltc sync` to replace your local ltc command-line tool with your target cluster's expected version.")) 498 }) 499 500 It("should print warning and NOT recommend sync if ServerVersions endpoint fails", func() { 501 fakeVersionManager.LtcMatchesServerReturns(false, errors.New("whoops")) 502 503 test_helpers.ExecuteCommandWithArgs(targetCommand, []string{"target.com"}) 504 505 Expect(fakeVersionManager.LtcMatchesServerCallCount()).To(Equal(1)) 506 Expect(fakeVersionManager.LtcMatchesServerArgsForCall(0)).To(Equal("http://receptor.target.com")) 507 508 Expect(outputBuffer).To(test_helpers.SayLine("WARNING: local ltc version (some-version) does not match target expected version.")) 509 Expect(outputBuffer).NotTo(test_helpers.SayLine("Run `ltc sync` to replace your local ltc command-line tool with your target cluster's expected version.")) 510 }) 511 512 It("should not print an error if ltc version matches server", func() { 513 fakeVersionManager.LtcMatchesServerReturns(true, nil) 514 515 test_helpers.ExecuteCommandWithArgs(targetCommand, []string{"target.com"}) 516 517 Expect(fakeVersionManager.LtcMatchesServerCallCount()).To(Equal(1)) 518 Expect(fakeVersionManager.LtcMatchesServerArgsForCall(0)).To(Equal("http://receptor.target.com")) 519 520 Expect(outputBuffer).NotTo(test_helpers.SayLine("WARNING: local ltc version (some-version) does not match target expected version.")) 521 Expect(outputBuffer).NotTo(test_helpers.SayLine("Run `ltc sync` to replace your local ltc command-line tool with your target cluster's expected version.")) 522 }) 523 }) 524 }) 525 }) 526 527 type errorPersister string 528 529 func (f errorPersister) Load(i interface{}) error { 530 return errors.New(string(f)) 531 } 532 533 func (f errorPersister) Save(i interface{}) error { 534 return errors.New(string(f)) 535 }