github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/proxyupdater/proxyupdater_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package proxyupdater_test 5 6 import ( 7 "fmt" 8 "net/http" 9 "net/http/httptest" 10 "os" 11 "path" 12 "path/filepath" 13 "strings" 14 "time" 15 16 "github.com/juju/errors" 17 "github.com/juju/loggo" 18 jujuos "github.com/juju/os/v2" 19 "github.com/juju/packaging/v3/commands" 20 pacconfig "github.com/juju/packaging/v3/config" 21 "github.com/juju/proxy" 22 jc "github.com/juju/testing/checkers" 23 "github.com/juju/worker/v3" 24 "github.com/juju/worker/v3/workertest" 25 gc "gopkg.in/check.v1" 26 27 proxyupdaterapi "github.com/juju/juju/api/agent/proxyupdater" 28 "github.com/juju/juju/core/watcher" 29 coretesting "github.com/juju/juju/testing" 30 "github.com/juju/juju/worker/proxyupdater" 31 ) 32 33 type ProxyUpdaterSuite struct { 34 coretesting.BaseSuite 35 36 api *fakeAPI 37 proxyEnvFile string 38 proxySystemdFile string 39 detectedSettings proxy.Settings 40 inProcSettings chan proxy.Settings 41 config proxyupdater.Config 42 } 43 44 var _ = gc.Suite(&ProxyUpdaterSuite{}) 45 46 func newNotAWatcher() notAWatcher { 47 return notAWatcher{workertest.NewFakeWatcher(2, 2)} 48 } 49 50 type notAWatcher struct { 51 workertest.NotAWatcher 52 } 53 54 func (w notAWatcher) Changes() watcher.NotifyChannel { 55 return w.NotAWatcher.Changes() 56 } 57 58 type fakeAPI struct { 59 proxies proxyupdaterapi.ProxyConfiguration 60 Err error 61 Watcher *notAWatcher 62 } 63 64 func NewFakeAPI() *fakeAPI { 65 f := &fakeAPI{} 66 return f 67 } 68 69 func (api fakeAPI) ProxyConfig() (proxyupdaterapi.ProxyConfiguration, error) { 70 return api.proxies, api.Err 71 } 72 73 func (api *fakeAPI) WatchForProxyConfigAndAPIHostPortChanges() (watcher.NotifyWatcher, error) { 74 if api.Watcher == nil { 75 w := newNotAWatcher() 76 api.Watcher = &w 77 } 78 return api.Watcher, nil 79 } 80 81 func (s *ProxyUpdaterSuite) SetUpTest(c *gc.C) { 82 s.BaseSuite.SetUpTest(c) 83 s.api = NewFakeAPI() 84 85 // Make buffer large for tests that never look at the settings. 86 s.inProcSettings = make(chan proxy.Settings, 1000) 87 88 directory := c.MkDir() 89 s.proxySystemdFile = filepath.Join(directory, "systemd.file") 90 s.proxyEnvFile = filepath.Join(directory, "env.file") 91 logger := loggo.GetLogger("test.proxyupdater") 92 logger.SetLogLevel(loggo.TRACE) 93 s.config = proxyupdater.Config{ 94 SupportLegacyValues: true, 95 SystemdFiles: []string{s.proxySystemdFile}, 96 EnvFiles: []string{s.proxyEnvFile}, 97 API: s.api, 98 InProcessUpdate: func(newSettings proxy.Settings) error { 99 select { 100 case s.inProcSettings <- newSettings: 101 case <-time.After(coretesting.LongWait): 102 panic("couldn't send settings on inProcSettings channel") 103 } 104 return nil 105 }, 106 Logger: logger, 107 } 108 s.PatchValue(&pacconfig.AptProxyConfigFile, path.Join(directory, "juju-apt-proxy")) 109 } 110 111 func (s *ProxyUpdaterSuite) TearDownTest(c *gc.C) { 112 s.BaseSuite.TearDownTest(c) 113 if s.api.Watcher != nil { 114 s.api.Watcher.Close() 115 } 116 } 117 118 func (s *ProxyUpdaterSuite) waitProxySettings(c *gc.C, expected proxy.Settings) { 119 maxWait := time.After(coretesting.LongWait) 120 var ( 121 inProcSettings, envSettings proxy.Settings 122 gotInProc, gotEnv bool 123 ) 124 for { 125 select { 126 case <-maxWait: 127 c.Fatalf("timeout while waiting for proxy settings to change") 128 return 129 case inProcSettings = <-s.inProcSettings: 130 if c.Check(inProcSettings, gc.Equals, expected) { 131 gotInProc = true 132 } 133 case <-time.After(coretesting.ShortWait): 134 envSettings = proxy.DetectProxies() 135 if envSettings == expected { 136 gotEnv = true 137 } else { 138 if envSettings != s.detectedSettings { 139 c.Logf("proxy settings are \n%#v, should be \n%#v, still waiting", envSettings, expected) 140 } 141 s.detectedSettings = envSettings 142 } 143 } 144 if gotEnv && gotInProc { 145 break 146 } 147 } 148 } 149 150 func (s *ProxyUpdaterSuite) waitForFile(c *gc.C, filename, expected string) { 151 maxWait := time.After(coretesting.LongWait) 152 for { 153 select { 154 case <-maxWait: 155 c.Fatalf("timeout while waiting for proxy settings to change") 156 return 157 case <-time.After(10 * time.Millisecond): 158 fileContent, err := os.ReadFile(filename) 159 if os.IsNotExist(err) { 160 continue 161 } 162 c.Assert(err, jc.ErrorIsNil) 163 if string(fileContent) != expected { 164 c.Logf("file content not matching, still waiting") 165 continue 166 } 167 return 168 } 169 } 170 } 171 172 func (s *ProxyUpdaterSuite) TestRunStop(c *gc.C) { 173 updater, err := proxyupdater.NewWorker(s.config) 174 c.Assert(err, jc.ErrorIsNil) 175 workertest.CleanKill(c, updater) 176 } 177 178 func (s *ProxyUpdaterSuite) useLegacyConfig(c *gc.C) (proxy.Settings, proxy.Settings) { 179 s.api.proxies = proxyupdaterapi.ProxyConfiguration{ 180 LegacyProxy: proxy.Settings{ 181 Http: "http legacy proxy", 182 Https: "https legacy proxy", 183 Ftp: "ftp legacy proxy", 184 NoProxy: "localhost,no legacy proxy", 185 }, 186 APTProxy: proxy.Settings{ 187 Http: "http://apt.http.proxy", 188 Https: "https://apt.https.proxy", 189 Ftp: "ftp://apt.ftp.proxy", 190 }, 191 } 192 193 return s.api.proxies.LegacyProxy, s.api.proxies.APTProxy 194 } 195 196 func (s *ProxyUpdaterSuite) useJujuConfig(c *gc.C) (proxy.Settings, proxy.Settings) { 197 s.api.proxies = proxyupdaterapi.ProxyConfiguration{ 198 JujuProxy: proxy.Settings{ 199 Http: "http juju proxy", 200 Https: "https juju proxy", 201 Ftp: "ftp juju proxy", 202 NoProxy: "localhost,no juju proxy", 203 }, 204 APTProxy: proxy.Settings{ 205 Http: "http://apt.http.proxy", 206 Https: "https://apt.https.proxy", 207 Ftp: "ftp://apt.ftp.proxy", 208 }, 209 } 210 211 return s.api.proxies.JujuProxy, s.api.proxies.APTProxy 212 } 213 214 func (s *ProxyUpdaterSuite) TestInitialStateLegacyProxy(c *gc.C) { 215 if host := jujuos.HostOS(); host == jujuos.CentOS { 216 c.Skip(fmt.Sprintf("apt settings not handled on %s", host.String())) 217 } 218 219 proxySettings, aptProxySettings := s.useLegacyConfig(c) 220 221 updater, err := proxyupdater.NewWorker(s.config) 222 c.Assert(err, jc.ErrorIsNil) 223 defer worker.Stop(updater) 224 225 s.waitProxySettings(c, proxySettings) 226 s.waitForFile(c, s.proxyEnvFile, proxySettings.AsScriptEnvironment()) 227 s.waitForFile(c, s.proxySystemdFile, proxySettings.AsSystemdDefaultEnv()) 228 229 paccmder := commands.NewAptPackageCommander() 230 s.waitForFile(c, pacconfig.AptProxyConfigFile, paccmder.ProxyConfigContents(aptProxySettings)+"\n") 231 } 232 233 func (s *ProxyUpdaterSuite) TestInitialStateJujuProxy(c *gc.C) { 234 if host := jujuos.HostOS(); host == jujuos.CentOS { 235 c.Skip(fmt.Sprintf("apt settings not handled on %s", host.String())) 236 } 237 238 proxySettings, aptProxySettings := s.useJujuConfig(c) 239 240 updater, err := proxyupdater.NewWorker(s.config) 241 c.Assert(err, jc.ErrorIsNil) 242 defer worker.Stop(updater) 243 244 s.waitProxySettings(c, proxySettings) 245 var empty proxy.Settings 246 // The environment files are written, but with empty content. 247 // This keeps the symlinks working. 248 s.waitForFile(c, s.proxyEnvFile, empty.AsScriptEnvironment()) 249 s.waitForFile(c, s.proxySystemdFile, empty.AsSystemdDefaultEnv()) 250 251 paccmder := commands.NewAptPackageCommander() 252 s.waitForFile(c, pacconfig.AptProxyConfigFile, paccmder.ProxyConfigContents(aptProxySettings)+"\n") 253 } 254 255 func (s *ProxyUpdaterSuite) TestEnvironmentVariablesLegacyProxy(c *gc.C) { 256 setenv := func(proxy, value string) { 257 os.Setenv(proxy, value) 258 os.Setenv(strings.ToUpper(proxy), value) 259 } 260 setenv("http_proxy", "foo") 261 setenv("https_proxy", "foo") 262 setenv("ftp_proxy", "foo") 263 setenv("no_proxy", "foo") 264 265 proxySettings, _ := s.useLegacyConfig(c) 266 updater, err := proxyupdater.NewWorker(s.config) 267 c.Assert(err, jc.ErrorIsNil) 268 defer worker.Stop(updater) 269 s.waitProxySettings(c, proxySettings) 270 271 assertEnv := func(proxy, value string) { 272 c.Assert(os.Getenv(proxy), gc.Equals, value) 273 c.Assert(os.Getenv(strings.ToUpper(proxy)), gc.Equals, value) 274 } 275 assertEnv("http_proxy", proxySettings.Http) 276 assertEnv("https_proxy", proxySettings.Https) 277 assertEnv("ftp_proxy", proxySettings.Ftp) 278 assertEnv("no_proxy", proxySettings.NoProxy) 279 } 280 281 func (s *ProxyUpdaterSuite) TestEnvironmentVariablesJujuProxy(c *gc.C) { 282 setenv := func(proxy, value string) { 283 os.Setenv(proxy, value) 284 os.Setenv(strings.ToUpper(proxy), value) 285 } 286 setenv("http_proxy", "foo") 287 setenv("https_proxy", "foo") 288 setenv("ftp_proxy", "foo") 289 setenv("no_proxy", "foo") 290 291 proxySettings, _ := s.useJujuConfig(c) 292 updater, err := proxyupdater.NewWorker(s.config) 293 c.Assert(err, jc.ErrorIsNil) 294 defer worker.Stop(updater) 295 s.waitProxySettings(c, proxySettings) 296 297 assertEnv := func(proxy, value string) { 298 c.Assert(os.Getenv(proxy), gc.Equals, value) 299 c.Assert(os.Getenv(strings.ToUpper(proxy)), gc.Equals, value) 300 } 301 assertEnv("http_proxy", proxySettings.Http) 302 assertEnv("https_proxy", proxySettings.Https) 303 assertEnv("ftp_proxy", proxySettings.Ftp) 304 assertEnv("no_proxy", proxySettings.NoProxy) 305 } 306 307 func (s *ProxyUpdaterSuite) TestExternalFuncCalled(c *gc.C) { 308 309 // Called for both legacy and juju proxy values 310 externalProxySet := func() proxy.Settings { 311 updated := make(chan proxy.Settings) 312 done := make(chan struct{}) 313 s.config.ExternalUpdate = func(values proxy.Settings) error { 314 select { 315 case updated <- values: 316 case <-done: 317 } 318 return nil 319 } 320 updater, err := proxyupdater.NewWorker(s.config) 321 c.Assert(err, jc.ErrorIsNil) 322 defer worker.Stop(updater) 323 // We need to close done before stopping the worker, so the 324 // defer comes after the worker stop. 325 defer close(done) 326 327 select { 328 case <-time.After(time.Second): 329 c.Fatal("function not called") 330 case externalSettings := <-updated: 331 return externalSettings 332 } 333 return proxy.Settings{} 334 } 335 336 proxySettings, _ := s.useLegacyConfig(c) 337 externalSettings := externalProxySet() 338 c.Assert(externalSettings, jc.DeepEquals, proxySettings) 339 340 proxySettings, _ = s.useJujuConfig(c) 341 externalSettings = externalProxySet() 342 c.Assert(externalSettings, jc.DeepEquals, proxySettings) 343 } 344 345 func (s *ProxyUpdaterSuite) TestErrorSettingInProcessLogs(c *gc.C) { 346 proxySettings, _ := s.useJujuConfig(c) 347 348 s.config.InProcessUpdate = func(newSettings proxy.Settings) error { 349 select { 350 case s.inProcSettings <- newSettings: 351 case <-time.After(coretesting.LongWait): 352 panic("couldn't send settings on inProcSettings channel") 353 } 354 return errors.New("gone daddy gone") 355 } 356 357 var logWriter loggo.TestWriter 358 c.Assert(loggo.RegisterWriter("proxyupdater-tests", &logWriter), jc.ErrorIsNil) 359 defer func() { 360 loggo.RemoveWriter("proxyupdater-tests") 361 logWriter.Clear() 362 }() 363 364 updater, err := proxyupdater.NewWorker(s.config) 365 c.Assert(err, jc.ErrorIsNil) 366 s.waitProxySettings(c, proxySettings) 367 workertest.CleanKill(c, updater) 368 369 var foundMessage bool 370 expectedMessage := "error updating in-process proxy settings: gone daddy gone" 371 for _, entry := range logWriter.Log() { 372 if entry.Level == loggo.ERROR && strings.Contains(entry.Message, expectedMessage) { 373 foundMessage = true 374 break 375 } 376 } 377 c.Assert(foundMessage, jc.IsTrue) 378 } 379 380 func nextCall(c *gc.C, calls <-chan []string) []string { 381 select { 382 case call := <-calls: 383 return call 384 case <-time.After(coretesting.LongWait): 385 c.Fatalf("run func not called") 386 } 387 panic("unreachable") 388 } 389 390 func (s *ProxyUpdaterSuite) TestSnapProxySetNoneSet(c *gc.C) { 391 if host := jujuos.HostOS(); host == jujuos.CentOS { 392 c.Skip(fmt.Sprintf("snap settings not handled on %s", host.String())) 393 } 394 395 logger := s.config.Logger 396 calls := make(chan []string) 397 s.config.RunFunc = func(in string, cmd string, args ...string) (string, error) { 398 logger.Debugf("RunFunc(%q, %q, %#v)", in, cmd, args) 399 calls <- append([]string{in, cmd}, args...) 400 return "", nil 401 } 402 403 s.api.proxies = proxyupdaterapi.ProxyConfiguration{} 404 405 updater, err := proxyupdater.NewWorker(s.config) 406 c.Assert(err, jc.ErrorIsNil) 407 defer workertest.CleanKill(c, updater) 408 409 // The worker doesn't precheck any of the snap proxy values, as it is expected 410 // that the set call is cheap. Every time the worker starts, we call set for the current 411 // values. 412 c.Assert(nextCall(c, calls), jc.DeepEquals, []string{"", "snap", "set", "system", 413 "proxy.http=", 414 "proxy.https=", 415 "proxy.store=", 416 }) 417 } 418 419 func (s *ProxyUpdaterSuite) TestSnapProxySet(c *gc.C) { 420 if host := jujuos.HostOS(); host == jujuos.CentOS { 421 c.Skip(fmt.Sprintf("snap settings not handled on %s", host.String())) 422 } 423 424 logger := s.config.Logger 425 calls := make(chan []string) 426 s.config.RunFunc = func(in string, cmd string, args ...string) (string, error) { 427 logger.Debugf("RunFunc(%q, %q, %#v)", in, cmd, args) 428 calls <- append([]string{in, cmd}, args...) 429 return "", nil 430 } 431 432 s.api.proxies = proxyupdaterapi.ProxyConfiguration{ 433 SnapProxy: proxy.Settings{ 434 Http: "http://snap-proxy", 435 Https: "https://snap-proxy", 436 }, 437 } 438 439 updater, err := proxyupdater.NewWorker(s.config) 440 c.Assert(err, jc.ErrorIsNil) 441 defer workertest.CleanKill(c, updater) 442 443 // The snap store is set to the empty string because as the agent is starting 444 // and it doesn't check to see what the store was set to, so to be sure, it just 445 // calls the set value. 446 c.Assert(nextCall(c, calls), jc.DeepEquals, []string{"", "snap", "set", "system", 447 "proxy.http=http://snap-proxy", 448 "proxy.https=https://snap-proxy", 449 "proxy.store=", 450 }) 451 } 452 453 func (s *ProxyUpdaterSuite) TestSnapStoreProxy(c *gc.C) { 454 if host := jujuos.HostOS(); host == jujuos.CentOS { 455 c.Skip(fmt.Sprintf("snap settings not handled on %s", host.String())) 456 } 457 458 logger := s.config.Logger 459 calls := make(chan []string) 460 s.config.RunFunc = func(in string, cmd string, args ...string) (string, error) { 461 logger.Debugf("RunFunc(%q, %q, %#v)", in, cmd, args) 462 calls <- append([]string{in, cmd}, args...) 463 return "", nil 464 } 465 466 s.api.proxies = proxyupdaterapi.ProxyConfiguration{ 467 SnapStoreProxyId: "42", 468 SnapStoreProxyAssertions: "please trust us", 469 } 470 471 updater, err := proxyupdater.NewWorker(s.config) 472 c.Assert(err, jc.ErrorIsNil) 473 defer workertest.CleanKill(c, updater) 474 475 c.Assert(nextCall(c, calls), jc.DeepEquals, []string{"please trust us", "snap", "ack", "/dev/stdin"}) 476 477 // The http and https proxy values are set to be empty as it is the first pass through. 478 c.Assert(nextCall(c, calls), jc.DeepEquals, []string{"", "snap", "set", "system", 479 "proxy.http=", 480 "proxy.https=", 481 "proxy.store=42", 482 }) 483 } 484 485 func (s *ProxyUpdaterSuite) TestSnapStoreProxyURL(c *gc.C) { 486 if host := jujuos.HostOS(); host == jujuos.CentOS { 487 c.Skip(fmt.Sprintf("snap settings not handled on %s", host.String())) 488 } 489 490 logger := s.config.Logger 491 calls := make(chan []string) 492 s.config.RunFunc = func(in string, cmd string, args ...string) (string, error) { 493 logger.Debugf("RunFunc(%q, %q, %#v)", in, cmd, args) 494 calls <- append([]string{in, cmd}, args...) 495 return "", nil 496 } 497 498 var ( 499 srv *httptest.Server 500 501 proxyRes = ` 502 type: store 503 authority-id: canonical 504 store: WhatDoesTheBigRedButtonDo 505 operator-id: 0123456789067OdMqoW9YLp3e0EgakQf 506 timestamp: 2019-08-27T12:20:45.166790Z 507 url: $url 508 sign-key-sha3-384: BWDEoaqyr25nF5SNCvEv2v7QnM9QsfCc0PBMYD_i2NGSQ32EF2d4D0hqUel3m8ul 509 510 DATA... 511 DATA... 512 ` 513 ) 514 515 srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 516 _, _ = w.Write([]byte(proxyRes)) 517 })) 518 proxyRes = strings.Replace(proxyRes, "$url", srv.URL, -1) 519 defer srv.Close() 520 521 s.api.proxies = proxyupdaterapi.ProxyConfiguration{ 522 SnapStoreProxyURL: srv.URL, 523 } 524 525 updater, err := proxyupdater.NewWorker(s.config) 526 c.Assert(err, jc.ErrorIsNil) 527 defer workertest.CleanKill(c, updater) 528 529 c.Assert(nextCall(c, calls), jc.DeepEquals, []string{proxyRes, "snap", "ack", "/dev/stdin"}) 530 531 // The http and https proxy values are set to be empty as it is the first pass through. 532 c.Assert(nextCall(c, calls), jc.DeepEquals, []string{"", "snap", "set", "system", 533 "proxy.http=", 534 "proxy.https=", 535 "proxy.store=WhatDoesTheBigRedButtonDo", 536 }) 537 } 538 539 func (s *ProxyUpdaterSuite) TestSnapStoreProxyURLOverridesManualAssertion(c *gc.C) { 540 if host := jujuos.HostOS(); host == jujuos.CentOS { 541 c.Skip(fmt.Sprintf("snap settings not handled on %s", host.String())) 542 } 543 544 logger := s.config.Logger 545 calls := make(chan []string) 546 s.config.RunFunc = func(in string, cmd string, args ...string) (string, error) { 547 logger.Debugf("RunFunc(%q, %q, %#v)", in, cmd, args) 548 calls <- append([]string{in, cmd}, args...) 549 return "", nil 550 } 551 552 var ( 553 srv *httptest.Server 554 555 proxyRes = ` 556 type: store 557 authority-id: canonical 558 store: WhatDoesTheBigRedButtonDo 559 operator-id: 0123456789067OdMqoW9YLp3e0EgakQf 560 timestamp: 2019-08-27T12:20:45.166790Z 561 url: $url 562 sign-key-sha3-384: BWDEoaqyr25nF5SNCvEv2v7QnM9QsfCc0PBMYD_i2NGSQ32EF2d4D0hqUel3m8ul 563 564 DATA... 565 DATA... 566 ` 567 ) 568 569 srv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 570 _, _ = w.Write([]byte(proxyRes)) 571 })) 572 proxyRes = strings.Replace(proxyRes, "$url", srv.URL, -1) 573 defer srv.Close() 574 575 s.api.proxies = proxyupdaterapi.ProxyConfiguration{ 576 SnapStoreProxyId: "42", 577 SnapStoreProxyAssertions: "please trust us", 578 SnapStoreProxyURL: srv.URL, 579 } 580 581 updater, err := proxyupdater.NewWorker(s.config) 582 c.Assert(err, jc.ErrorIsNil) 583 defer workertest.CleanKill(c, updater) 584 585 c.Assert(nextCall(c, calls), jc.DeepEquals, []string{proxyRes, "snap", "ack", "/dev/stdin"}) 586 587 // The http and https proxy values are set to be empty as it is the first pass through. 588 c.Assert(nextCall(c, calls), jc.DeepEquals, []string{"", "snap", "set", "system", 589 "proxy.http=", 590 "proxy.https=", 591 "proxy.store=WhatDoesTheBigRedButtonDo", 592 }) 593 } 594 595 func (s *ProxyUpdaterSuite) TestAptMirror(c *gc.C) { 596 if host := jujuos.HostOS(); host == jujuos.CentOS { 597 c.Skip(fmt.Sprintf("apt mirror not supported on %s", host.String())) 598 } 599 600 logger := s.config.Logger 601 calls := make(chan []string) 602 s.config.RunFunc = func(in string, cmd string, args ...string) (string, error) { 603 logger.Debugf("RunFunc(%q, %q, %#v)", in, cmd, args) 604 calls <- append([]string{in, cmd}, args...) 605 return "", nil 606 } 607 608 s.api.proxies = proxyupdaterapi.ProxyConfiguration{ 609 AptMirror: "http://mirror", 610 } 611 612 updater, err := proxyupdater.NewWorker(s.config) 613 c.Assert(err, jc.ErrorIsNil) 614 defer workertest.CleanKill(c, updater) 615 616 c.Assert(nextCall(c, calls), jc.DeepEquals, []string{"", "snap", "set", "system", 617 "proxy.http=", 618 "proxy.https=", 619 "proxy.store=", 620 }) 621 c.Assert(nextCall(c, calls), jc.DeepEquals, []string{"", "/bin/bash", "-c", ` 622 #!/bin/bash 623 set -e 624 ( 625 old_archive_mirror=$(awk "/^deb .* $(awk -F= '/DISTRIB_CODENAME=/ {gsub(/"/,""); print $2}' /etc/lsb-release) .*main.*\$/{print \$2;exit}" /etc/apt/sources.list) 626 new_archive_mirror=http://mirror 627 sed -i s,$old_archive_mirror,$new_archive_mirror, /etc/apt/sources.list 628 old_prefix=/var/lib/apt/lists/$(echo $old_archive_mirror | sed 's,.*://,,' | sed 's,/$,,' | tr / _) 629 new_prefix=/var/lib/apt/lists/$(echo $new_archive_mirror | sed 's,.*://,,' | sed 's,/$,,' | tr / _) 630 [ "$old_prefix" != "$new_prefix" ] && 631 for old in ${old_prefix}_*; do 632 new=$(echo $old | sed s,^$old_prefix,$new_prefix,) 633 if [ -f $old ]; then 634 mv $old $new 635 fi 636 done 637 old_security_mirror=$(awk "/^deb .* $(awk -F= '/DISTRIB_CODENAME=/ {gsub(/"/,""); print $2}' /etc/lsb-release)-security .*main.*\$/{print \$2;exit}" /etc/apt/sources.list) 638 new_security_mirror=http://mirror 639 sed -i s,$old_security_mirror,$new_security_mirror, /etc/apt/sources.list 640 old_prefix=/var/lib/apt/lists/$(echo $old_security_mirror | sed 's,.*://,,' | sed 's,/$,,' | tr / _) 641 new_prefix=/var/lib/apt/lists/$(echo $new_security_mirror | sed 's,.*://,,' | sed 's,/$,,' | tr / _) 642 [ "$old_prefix" != "$new_prefix" ] && 643 for old in ${old_prefix}_*; do 644 new=$(echo $old | sed s,^$old_prefix,$new_prefix,) 645 if [ -f $old ]; then 646 mv $old $new 647 fi 648 done 649 )`[1:], 650 }) 651 }