func validateList(t *testing.T, lister Lister, user user.Info, expectedSet util.StringSet) { namespaceList, err := lister.List(user) if err != nil { t.Errorf("Unexpected error %v", err) } results := util.StringSet{} for _, namespace := range namespaceList.Items { results.Insert(namespace.Name) } if results.Len() != expectedSet.Len() || !results.HasAll(expectedSet.List()...) { t.Errorf("User %v, Expected: %v, Actual: %v", user.GetName(), expectedSet, results) } }
func TestHammerController(t *testing.T) { // This test executes a bunch of requests through the fake source and // controller framework to make sure there's no locking/threading // errors. If an error happens, it should hang forever or trigger the // race detector. // source simulates an apiserver object endpoint. source := framework.NewFakeControllerSource() // Let's do threadsafe output to get predictable test results. outputSetLock := sync.Mutex{} // map of key to operations done on the key outputSet := map[string][]string{} recordFunc := func(eventType string, obj interface{}) { key, err := framework.DeletionHandlingMetaNamespaceKeyFunc(obj) if err != nil { t.Errorf("something wrong with key: %v", err) key = "oops something went wrong with the key" } // Record some output when items are deleted. outputSetLock.Lock() defer outputSetLock.Unlock() outputSet[key] = append(outputSet[key], eventType) } // Make a controller which just logs all the changes it gets. _, controller := framework.NewInformer( source, &api.Pod{}, time.Millisecond*100, framework.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { recordFunc("add", obj) }, UpdateFunc: func(oldObj, newObj interface{}) { recordFunc("update", newObj) }, DeleteFunc: func(obj interface{}) { recordFunc("delete", obj) }, }, ) // Run the controller and run it until we close stop. stop := make(chan struct{}) go controller.Run(stop) wg := sync.WaitGroup{} const threads = 3 wg.Add(threads) for i := 0; i < threads; i++ { go func() { defer wg.Done() // Let's add a few objects to the source. currentNames := util.StringSet{} rs := rand.NewSource(rand.Int63()) f := fuzz.New().NilChance(.5).NumElements(0, 2).RandSource(rs) r := rand.New(rs) // Mustn't use r and f concurrently! for i := 0; i < 100; i++ { var name string var isNew bool if currentNames.Len() == 0 || r.Intn(3) == 1 { f.Fuzz(&name) isNew = true } else { l := currentNames.List() name = l[r.Intn(len(l))] } pod := &api.Pod{} f.Fuzz(pod) pod.ObjectMeta.Name = name pod.ObjectMeta.Namespace = "default" // Add, update, or delete randomly. // Note that these pods are not valid-- the fake source doesn't // call validation or perform any other checking. if isNew { currentNames.Insert(name) source.Add(pod) continue } switch r.Intn(2) { case 0: currentNames.Insert(name) source.Modify(pod) case 1: currentNames.Delete(name) source.Delete(pod) } } }() } wg.Wait() // Let's wait for the controller to finish processing the things we just added. time.Sleep(100 * time.Millisecond) close(stop) outputSetLock.Lock() t.Logf("got: %#v", outputSet) }
func NewCmdPruneImages(f *clientcmd.Factory, parentName, name string, out io.Writer) *cobra.Command { cfg := &pruneImagesConfig{ Confirm: false, KeepYoungerThan: 60 * time.Minute, TagRevisionsToKeep: 3, } cmd := &cobra.Command{ Use: name, Short: "Remove unreferenced images", Long: fmt.Sprintf(imagesLongDesc, parentName, name), Run: func(cmd *cobra.Command, args []string) { if len(args) > 0 { glog.Fatal("No arguments are allowed to this command") } osClient, kClient, registryClient, err := getClients(f, cfg) cmdutil.CheckErr(err) allImages, err := osClient.Images().List(labels.Everything(), fields.Everything()) cmdutil.CheckErr(err) allStreams, err := osClient.ImageStreams(kapi.NamespaceAll).List(labels.Everything(), fields.Everything()) cmdutil.CheckErr(err) allPods, err := kClient.Pods(kapi.NamespaceAll).List(labels.Everything(), fields.Everything()) cmdutil.CheckErr(err) allRCs, err := kClient.ReplicationControllers(kapi.NamespaceAll).List(labels.Everything()) cmdutil.CheckErr(err) allBCs, err := osClient.BuildConfigs(kapi.NamespaceAll).List(labels.Everything(), fields.Everything()) cmdutil.CheckErr(err) allBuilds, err := osClient.Builds(kapi.NamespaceAll).List(labels.Everything(), fields.Everything()) cmdutil.CheckErr(err) allDCs, err := osClient.DeploymentConfigs(kapi.NamespaceAll).List(labels.Everything(), fields.Everything()) cmdutil.CheckErr(err) pruner := prune.NewImagePruner( cfg.KeepYoungerThan, cfg.TagRevisionsToKeep, allImages, allStreams, allPods, allRCs, allBCs, allBuilds, allDCs, ) w := tabwriter.NewWriter(out, 10, 4, 3, ' ', 0) defer w.Flush() var streams util.StringSet printImageHeader := true describingImagePruneFunc := func(image *imageapi.Image) error { if printImageHeader { printImageHeader = false fmt.Fprintf(w, "IMAGE\tSTREAMS") } if streams.Len() > 0 { fmt.Fprintf(w, strings.Join(streams.List(), ", ")) } fmt.Fprintf(w, "\n%s\t", image.Name) streams = util.NewStringSet() return nil } describingImageStreamPruneFunc := func(stream *imageapi.ImageStream, image *imageapi.Image) (*imageapi.ImageStream, error) { streams.Insert(stream.Status.DockerImageRepository) return stream, nil } printLayerHeader := true describingLayerPruneFunc := func(registryURL, repo, layer string) error { if printLayerHeader { printLayerHeader = false // need to print the remaining streams for the last image if streams.Len() > 0 { fmt.Fprintf(w, strings.Join(streams.List(), ", ")) } fmt.Fprintf(w, "\n\nREGISTRY\tSTREAM\tLAYER\n") } fmt.Fprintf(w, "%s\t%s\t%s\n", registryURL, repo, layer) return nil } var ( imagePruneFunc prune.ImagePruneFunc imageStreamPruneFunc prune.ImageStreamPruneFunc layerPruneFunc prune.LayerPruneFunc blobPruneFunc prune.BlobPruneFunc manifestPruneFunc prune.ManifestPruneFunc ) switch cfg.Confirm { case true: imagePruneFunc = func(image *imageapi.Image) error { describingImagePruneFunc(image) return prune.DeletingImagePruneFunc(osClient.Images())(image) } imageStreamPruneFunc = func(stream *imageapi.ImageStream, image *imageapi.Image) (*imageapi.ImageStream, error) { describingImageStreamPruneFunc(stream, image) return prune.DeletingImageStreamPruneFunc(osClient)(stream, image) } layerPruneFunc = func(registryURL, repo, layer string) error { describingLayerPruneFunc(registryURL, repo, layer) return prune.DeletingLayerPruneFunc(registryClient)(registryURL, repo, layer) } blobPruneFunc = prune.DeletingBlobPruneFunc(registryClient) manifestPruneFunc = prune.DeletingManifestPruneFunc(registryClient) default: fmt.Fprintln(os.Stderr, "Dry run enabled - no modifications will be made.") imagePruneFunc = describingImagePruneFunc imageStreamPruneFunc = describingImageStreamPruneFunc layerPruneFunc = describingLayerPruneFunc blobPruneFunc = func(registryURL, blob string) error { return nil } manifestPruneFunc = func(registryURL, repo, manifest string) error { return nil } } if len(cfg.RegistryUrlOverride) > 0 { originalLayerPruneFunc := layerPruneFunc layerPruneFunc = func(registryURL, repo, layer string) error { return originalLayerPruneFunc(cfg.RegistryUrlOverride, repo, layer) } originalBlobPruneFunc := blobPruneFunc blobPruneFunc = func(registryURL, blob string) error { return originalBlobPruneFunc(cfg.RegistryUrlOverride, blob) } originalManifestPruneFunc := manifestPruneFunc manifestPruneFunc = func(registryURL, repo, manifest string) error { return originalManifestPruneFunc(cfg.RegistryUrlOverride, repo, manifest) } } pruner.Run(imagePruneFunc, imageStreamPruneFunc, layerPruneFunc, blobPruneFunc, manifestPruneFunc) }, } cmd.Flags().BoolVar(&cfg.Confirm, "confirm", cfg.Confirm, "Specify that image pruning should proceed. Defaults to false, displaying what would be deleted but not actually deleting anything.") cmd.Flags().DurationVar(&cfg.KeepYoungerThan, "keep-younger-than", cfg.KeepYoungerThan, "Specify the minimum age of a build for it to be considered a candidate for pruning.") cmd.Flags().IntVar(&cfg.TagRevisionsToKeep, "keep-tag-revisions", cfg.TagRevisionsToKeep, "Specify the number of image revisions for a tag in an image stream that will be preserved.") cmd.Flags().StringVar(&cfg.CABundle, "certificate-authority", cfg.CABundle, "The path to a certificate authority bundle to use when communicating with the OpenShift-managed registries. Defaults to the certificate authority data from the current user's config file.") cmd.Flags().StringVar(&cfg.RegistryUrlOverride, "registry-url", cfg.RegistryUrlOverride, "The address to use when contacting the registry, instead of using the default value. This is useful if you can't resolve or reach the registry (e.g.; the default is a cluster-internal URL) but you do have an alternative route that works.") return cmd }