mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	Merge pull request #29388 from ronnielai/image-gc-check
Automatic merge from submit-queue Avoiding trying to gc images with no tags which are still in use #29325
This commit is contained in:
		| @@ -95,11 +95,12 @@ func ConvertPodStatusToRunningPod(podStatus *PodStatus) Pod { | |||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		container := &Container{ | 		container := &Container{ | ||||||
| 			ID:    containerStatus.ID, | 			ID:      containerStatus.ID, | ||||||
| 			Name:  containerStatus.Name, | 			Name:    containerStatus.Name, | ||||||
| 			Image: containerStatus.Image, | 			Image:   containerStatus.Image, | ||||||
| 			Hash:  containerStatus.Hash, | 			ImageID: containerStatus.ImageID, | ||||||
| 			State: containerStatus.State, | 			Hash:    containerStatus.Hash, | ||||||
|  | 			State:   containerStatus.State, | ||||||
| 		} | 		} | ||||||
| 		runningPod.Containers = append(runningPod.Containers, container) | 		runningPod.Containers = append(runningPod.Containers, container) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -245,6 +245,8 @@ type Container struct { | |||||||
| 	// The image name of the container, this also includes the tag of the image, | 	// The image name of the container, this also includes the tag of the image, | ||||||
| 	// the expected form is "NAME:TAG". | 	// the expected form is "NAME:TAG". | ||||||
| 	Image string | 	Image string | ||||||
|  | 	// The id of the image used by the container. | ||||||
|  | 	ImageID string | ||||||
| 	// Hash of the container, used for comparison. Optional for containers | 	// Hash of the container, used for comparison. Optional for containers | ||||||
| 	// not managed by kubelet. | 	// not managed by kubelet. | ||||||
| 	Hash uint64 | 	Hash uint64 | ||||||
|   | |||||||
| @@ -56,10 +56,11 @@ func toRuntimeContainer(c *dockertypes.Container) (*kubecontainer.Container, err | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return &kubecontainer.Container{ | 	return &kubecontainer.Container{ | ||||||
| 		ID:    kubecontainer.DockerID(c.ID).ContainerID(), | 		ID:      kubecontainer.DockerID(c.ID).ContainerID(), | ||||||
| 		Name:  dockerName.ContainerName, | 		Name:    dockerName.ContainerName, | ||||||
| 		Image: c.Image, | 		Image:   c.Image, | ||||||
| 		Hash:  hash, | 		ImageID: c.ImageID, | ||||||
|  | 		Hash:    hash, | ||||||
| 		// (random-liu) docker uses status to indicate whether a container is running or exited. | 		// (random-liu) docker uses status to indicate whether a container is running or exited. | ||||||
| 		// However, in kubernetes we usually use state to indicate whether a container is running or exited, | 		// However, in kubernetes we usually use state to indicate whether a container is running or exited, | ||||||
| 		// while use status to indicate the comprehensive status of the container. So we have different naming | 		// while use status to indicate the comprehensive status of the container. So we have different naming | ||||||
|   | |||||||
| @@ -170,8 +170,8 @@ func (im *realImageManager) detectImages(detectTime time.Time) error { | |||||||
| 	imagesInUse := sets.NewString() | 	imagesInUse := sets.NewString() | ||||||
| 	for _, pod := range pods { | 	for _, pod := range pods { | ||||||
| 		for _, container := range pod.Containers { | 		for _, container := range pod.Containers { | ||||||
| 			glog.V(5).Infof("Pod %s/%s, container %s uses image %s", pod.Namespace, pod.Name, container.Name, container.Image) | 			glog.V(5).Infof("Pod %s/%s, container %s uses image %s(%s)", pod.Namespace, pod.Name, container.Name, container.Image, container.ImageID) | ||||||
| 			imagesInUse.Insert(container.Image) | 			imagesInUse.Insert(container.ImageID) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -341,14 +341,9 @@ func (ev byLastUsedAndDetected) Less(i, j int) bool { | |||||||
| } | } | ||||||
|  |  | ||||||
| func isImageUsed(image container.Image, imagesInUse sets.String) bool { | func isImageUsed(image container.Image, imagesInUse sets.String) bool { | ||||||
| 	// Check the image ID and all the RepoTags and RepoDigests. | 	// Check the image ID. | ||||||
| 	if _, ok := imagesInUse[image.ID]; ok { | 	if _, ok := imagesInUse[image.ID]; ok { | ||||||
| 		return true | 		return true | ||||||
| 	} | 	} | ||||||
| 	for _, tag := range append(image.RepoTags, image.RepoDigests...) { |  | ||||||
| 		if _, ok := imagesInUse[tag]; ok { |  | ||||||
| 			return true |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return false | 	return false | ||||||
| } | } | ||||||
|   | |||||||
| @@ -59,15 +59,20 @@ func (im *realImageManager) getImageRecord(name string) (*imageRecord, bool) { | |||||||
| 	return &vCopy, ok | 	return &vCopy, ok | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Returns the id of the image with the given ID. | ||||||
|  | func imageID(id int) string { | ||||||
|  | 	return fmt.Sprintf("image-%d", id) | ||||||
|  | } | ||||||
|  |  | ||||||
| // Returns the name of the image with the given ID. | // Returns the name of the image with the given ID. | ||||||
| func imageName(id int) string { | func imageName(id int) string { | ||||||
| 	return fmt.Sprintf("image-%d", id) | 	return imageID(id) + "-name" | ||||||
| } | } | ||||||
|  |  | ||||||
| // Make an image with the specified ID. | // Make an image with the specified ID. | ||||||
| func makeImage(id int, size int64) container.Image { | func makeImage(id int, size int64) container.Image { | ||||||
| 	return container.Image{ | 	return container.Image{ | ||||||
| 		ID:   imageName(id), | 		ID:   imageID(id), | ||||||
| 		Size: size, | 		Size: size, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -75,8 +80,9 @@ func makeImage(id int, size int64) container.Image { | |||||||
| // Make a container with the specified ID. It will use the image with the same ID. | // Make a container with the specified ID. It will use the image with the same ID. | ||||||
| func makeContainer(id int) *container.Container { | func makeContainer(id int) *container.Container { | ||||||
| 	return &container.Container{ | 	return &container.Container{ | ||||||
| 		ID:    container.ContainerID{Type: "test", ID: fmt.Sprintf("container-%d", id)}, | 		ID:      container.ContainerID{Type: "test", ID: fmt.Sprintf("container-%d", id)}, | ||||||
| 		Image: imageName(id), | 		Image:   imageName(id), | ||||||
|  | 		ImageID: imageID(id), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -85,11 +91,21 @@ func TestDetectImagesInitialDetect(t *testing.T) { | |||||||
| 	fakeRuntime.ImageList = []container.Image{ | 	fakeRuntime.ImageList = []container.Image{ | ||||||
| 		makeImage(0, 1024), | 		makeImage(0, 1024), | ||||||
| 		makeImage(1, 2048), | 		makeImage(1, 2048), | ||||||
|  | 		makeImage(2, 2048), | ||||||
| 	} | 	} | ||||||
| 	fakeRuntime.AllPodList = []*containertest.FakePod{ | 	fakeRuntime.AllPodList = []*containertest.FakePod{ | ||||||
| 		{Pod: &container.Pod{ | 		{Pod: &container.Pod{ | ||||||
| 			Containers: []*container.Container{ | 			Containers: []*container.Container{ | ||||||
| 				makeContainer(1), | 				{ | ||||||
|  | 					ID:      container.ContainerID{Type: "test", ID: fmt.Sprintf("container-%d", 1)}, | ||||||
|  | 					ImageID: imageID(1), | ||||||
|  | 					// The image filed is not set to simulate a no-name image | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					ID:      container.ContainerID{Type: "test", ID: fmt.Sprintf("container-%d", 2)}, | ||||||
|  | 					Image:   imageName(2), | ||||||
|  | 					ImageID: imageID(2), | ||||||
|  | 				}, | ||||||
| 			}, | 			}, | ||||||
| 		}}, | 		}}, | ||||||
| 	} | 	} | ||||||
| @@ -98,12 +114,16 @@ func TestDetectImagesInitialDetect(t *testing.T) { | |||||||
| 	err := manager.detectImages(zero) | 	err := manager.detectImages(zero) | ||||||
| 	assert := assert.New(t) | 	assert := assert.New(t) | ||||||
| 	require.NoError(t, err) | 	require.NoError(t, err) | ||||||
| 	assert.Equal(manager.imageRecordsLen(), 2) | 	assert.Equal(manager.imageRecordsLen(), 3) | ||||||
| 	noContainer, ok := manager.getImageRecord(imageName(0)) | 	noContainer, ok := manager.getImageRecord(imageID(0)) | ||||||
| 	require.True(t, ok) | 	require.True(t, ok) | ||||||
| 	assert.Equal(zero, noContainer.firstDetected) | 	assert.Equal(zero, noContainer.firstDetected) | ||||||
| 	assert.Equal(zero, noContainer.lastUsed) | 	assert.Equal(zero, noContainer.lastUsed) | ||||||
| 	withContainer, ok := manager.getImageRecord(imageName(1)) | 	withContainerUsingNoNameImage, ok := manager.getImageRecord(imageID(1)) | ||||||
|  | 	require.True(t, ok) | ||||||
|  | 	assert.Equal(zero, withContainerUsingNoNameImage.firstDetected) | ||||||
|  | 	assert.True(withContainerUsingNoNameImage.lastUsed.After(startTime)) | ||||||
|  | 	withContainer, ok := manager.getImageRecord(imageID(2)) | ||||||
| 	require.True(t, ok) | 	require.True(t, ok) | ||||||
| 	assert.Equal(zero, withContainer.firstDetected) | 	assert.Equal(zero, withContainer.firstDetected) | ||||||
| 	assert.True(withContainer.lastUsed.After(startTime)) | 	assert.True(withContainer.lastUsed.After(startTime)) | ||||||
| @@ -141,15 +161,15 @@ func TestDetectImagesWithNewImage(t *testing.T) { | |||||||
| 	err = manager.detectImages(detectedTime) | 	err = manager.detectImages(detectedTime) | ||||||
| 	require.NoError(t, err) | 	require.NoError(t, err) | ||||||
| 	assert.Equal(manager.imageRecordsLen(), 3) | 	assert.Equal(manager.imageRecordsLen(), 3) | ||||||
| 	noContainer, ok := manager.getImageRecord(imageName(0)) | 	noContainer, ok := manager.getImageRecord(imageID(0)) | ||||||
| 	require.True(t, ok) | 	require.True(t, ok) | ||||||
| 	assert.Equal(zero, noContainer.firstDetected) | 	assert.Equal(zero, noContainer.firstDetected) | ||||||
| 	assert.Equal(zero, noContainer.lastUsed) | 	assert.Equal(zero, noContainer.lastUsed) | ||||||
| 	withContainer, ok := manager.getImageRecord(imageName(1)) | 	withContainer, ok := manager.getImageRecord(imageID(1)) | ||||||
| 	require.True(t, ok) | 	require.True(t, ok) | ||||||
| 	assert.Equal(zero, withContainer.firstDetected) | 	assert.Equal(zero, withContainer.firstDetected) | ||||||
| 	assert.True(withContainer.lastUsed.After(startTime)) | 	assert.True(withContainer.lastUsed.After(startTime)) | ||||||
| 	newContainer, ok := manager.getImageRecord(imageName(2)) | 	newContainer, ok := manager.getImageRecord(imageID(2)) | ||||||
| 	require.True(t, ok) | 	require.True(t, ok) | ||||||
| 	assert.Equal(detectedTime, newContainer.firstDetected) | 	assert.Equal(detectedTime, newContainer.firstDetected) | ||||||
| 	assert.Equal(zero, noContainer.lastUsed) | 	assert.Equal(zero, noContainer.lastUsed) | ||||||
| @@ -173,7 +193,7 @@ func TestDetectImagesContainerStopped(t *testing.T) { | |||||||
| 	assert := assert.New(t) | 	assert := assert.New(t) | ||||||
| 	require.NoError(t, err) | 	require.NoError(t, err) | ||||||
| 	assert.Equal(manager.imageRecordsLen(), 2) | 	assert.Equal(manager.imageRecordsLen(), 2) | ||||||
| 	withContainer, ok := manager.getImageRecord(imageName(1)) | 	withContainer, ok := manager.getImageRecord(imageID(1)) | ||||||
| 	require.True(t, ok) | 	require.True(t, ok) | ||||||
|  |  | ||||||
| 	// Simulate container being stopped. | 	// Simulate container being stopped. | ||||||
| @@ -181,11 +201,11 @@ func TestDetectImagesContainerStopped(t *testing.T) { | |||||||
| 	err = manager.detectImages(time.Now()) | 	err = manager.detectImages(time.Now()) | ||||||
| 	require.NoError(t, err) | 	require.NoError(t, err) | ||||||
| 	assert.Equal(manager.imageRecordsLen(), 2) | 	assert.Equal(manager.imageRecordsLen(), 2) | ||||||
| 	container1, ok := manager.getImageRecord(imageName(0)) | 	container1, ok := manager.getImageRecord(imageID(0)) | ||||||
| 	require.True(t, ok) | 	require.True(t, ok) | ||||||
| 	assert.Equal(zero, container1.firstDetected) | 	assert.Equal(zero, container1.firstDetected) | ||||||
| 	assert.Equal(zero, container1.lastUsed) | 	assert.Equal(zero, container1.lastUsed) | ||||||
| 	container2, ok := manager.getImageRecord(imageName(1)) | 	container2, ok := manager.getImageRecord(imageID(1)) | ||||||
| 	require.True(t, ok) | 	require.True(t, ok) | ||||||
| 	assert.Equal(zero, container2.firstDetected) | 	assert.Equal(zero, container2.firstDetected) | ||||||
| 	assert.True(container2.lastUsed.Equal(withContainer.lastUsed)) | 	assert.True(container2.lastUsed.Equal(withContainer.lastUsed)) | ||||||
| @@ -331,62 +351,6 @@ func TestFreeSpaceTiesBrokenByDetectedTime(t *testing.T) { | |||||||
| 	assert.Len(fakeRuntime.ImageList, 1) | 	assert.Len(fakeRuntime.ImageList, 1) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestFreeSpaceImagesAlsoDoesLookupByRepoTags(t *testing.T) { |  | ||||||
| 	manager, fakeRuntime, _ := newRealImageManager(ImageGCPolicy{}) |  | ||||||
| 	fakeRuntime.ImageList = []container.Image{ |  | ||||||
| 		makeImage(0, 1024), |  | ||||||
| 		{ |  | ||||||
| 			ID:       "5678", |  | ||||||
| 			RepoTags: []string{"potato", "salad"}, |  | ||||||
| 			Size:     2048, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	fakeRuntime.AllPodList = []*containertest.FakePod{ |  | ||||||
| 		{Pod: &container.Pod{ |  | ||||||
| 			Containers: []*container.Container{ |  | ||||||
| 				{ |  | ||||||
| 					ID:    container.ContainerID{Type: "test", ID: "c5678"}, |  | ||||||
| 					Image: "salad", |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 		}}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	spaceFreed, err := manager.freeSpace(1024, time.Now()) |  | ||||||
| 	assert := assert.New(t) |  | ||||||
| 	require.NoError(t, err) |  | ||||||
| 	assert.EqualValues(1024, spaceFreed) |  | ||||||
| 	assert.Len(fakeRuntime.ImageList, 1) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestFreeSpaceImagesAlsoDoesLookupByRepoDigests(t *testing.T) { |  | ||||||
| 	manager, fakeRuntime, _ := newRealImageManager(ImageGCPolicy{}) |  | ||||||
| 	fakeRuntime.ImageList = []container.Image{ |  | ||||||
| 		makeImage(0, 1024), |  | ||||||
| 		{ |  | ||||||
| 			ID:          "5678", |  | ||||||
| 			RepoDigests: []string{"potato", "salad"}, |  | ||||||
| 			Size:        2048, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
| 	fakeRuntime.AllPodList = []*containertest.FakePod{ |  | ||||||
| 		{Pod: &container.Pod{ |  | ||||||
| 			Containers: []*container.Container{ |  | ||||||
| 				{ |  | ||||||
| 					ID:    container.ContainerID{Type: "test", ID: "c5678"}, |  | ||||||
| 					Image: "salad", |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 		}}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	spaceFreed, err := manager.freeSpace(1024, time.Now()) |  | ||||||
| 	assert := assert.New(t) |  | ||||||
| 	require.NoError(t, err) |  | ||||||
| 	assert.EqualValues(1024, spaceFreed) |  | ||||||
| 	assert.Len(fakeRuntime.ImageList, 1) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestGarbageCollectBelowLowThreshold(t *testing.T) { | func TestGarbageCollectBelowLowThreshold(t *testing.T) { | ||||||
| 	policy := ImageGCPolicy{ | 	policy := ImageGCPolicy{ | ||||||
| 		HighThresholdPercent: 90, | 		HighThresholdPercent: 90, | ||||||
|   | |||||||
| @@ -1528,9 +1528,10 @@ func (r *Runtime) convertRktPod(rktpod *rktapi.Pod) (*kubecontainer.Pod, error) | |||||||
| 			ID:   buildContainerID(&containerID{rktpod.Id, app.Name}), | 			ID:   buildContainerID(&containerID{rktpod.Id, app.Name}), | ||||||
| 			Name: app.Name, | 			Name: app.Name, | ||||||
| 			// By default, the version returned by rkt API service will be "latest" if not specified. | 			// By default, the version returned by rkt API service will be "latest" if not specified. | ||||||
| 			Image: fmt.Sprintf("%s:%s", app.Image.Name, app.Image.Version), | 			Image:   fmt.Sprintf("%s:%s", app.Image.Name, app.Image.Version), | ||||||
| 			Hash:  containerHash, | 			ImageID: app.Image.Id, | ||||||
| 			State: appStateToContainerState(app.State), | 			Hash:    containerHash, | ||||||
|  | 			State:   appStateToContainerState(app.State), | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -374,18 +374,20 @@ func TestGetPods(t *testing.T) { | |||||||
| 					Namespace: "default", | 					Namespace: "default", | ||||||
| 					Containers: []*kubecontainer.Container{ | 					Containers: []*kubecontainer.Container{ | ||||||
| 						{ | 						{ | ||||||
| 							ID:    kubecontainer.BuildContainerID("rkt", "uuid-4002:app-1"), | 							ID:      kubecontainer.BuildContainerID("rkt", "uuid-4002:app-1"), | ||||||
| 							Name:  "app-1", | 							Name:    "app-1", | ||||||
| 							Image: "img-name-1:latest", | 							Image:   "img-name-1:latest", | ||||||
| 							Hash:  1001, | 							ImageID: "img-id-1", | ||||||
| 							State: "running", | 							Hash:    1001, | ||||||
|  | 							State:   "running", | ||||||
| 						}, | 						}, | ||||||
| 						{ | 						{ | ||||||
| 							ID:    kubecontainer.BuildContainerID("rkt", "uuid-4002:app-2"), | 							ID:      kubecontainer.BuildContainerID("rkt", "uuid-4002:app-2"), | ||||||
| 							Name:  "app-2", | 							Name:    "app-2", | ||||||
| 							Image: "img-name-2:latest", | 							Image:   "img-name-2:latest", | ||||||
| 							Hash:  1002, | 							ImageID: "img-id-2", | ||||||
| 							State: "exited", | 							Hash:    1002, | ||||||
|  | 							State:   "exited", | ||||||
| 						}, | 						}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| @@ -435,18 +437,20 @@ func TestGetPods(t *testing.T) { | |||||||
| 					Namespace: "default", | 					Namespace: "default", | ||||||
| 					Containers: []*kubecontainer.Container{ | 					Containers: []*kubecontainer.Container{ | ||||||
| 						{ | 						{ | ||||||
| 							ID:    kubecontainer.BuildContainerID("rkt", "uuid-4002:app-1"), | 							ID:      kubecontainer.BuildContainerID("rkt", "uuid-4002:app-1"), | ||||||
| 							Name:  "app-1", | 							Name:    "app-1", | ||||||
| 							Image: "img-name-1:latest", | 							Image:   "img-name-1:latest", | ||||||
| 							Hash:  1001, | 							ImageID: "img-id-1", | ||||||
| 							State: "running", | 							Hash:    1001, | ||||||
|  | 							State:   "running", | ||||||
| 						}, | 						}, | ||||||
| 						{ | 						{ | ||||||
| 							ID:    kubecontainer.BuildContainerID("rkt", "uuid-4002:app-2"), | 							ID:      kubecontainer.BuildContainerID("rkt", "uuid-4002:app-2"), | ||||||
| 							Name:  "app-2", | 							Name:    "app-2", | ||||||
| 							Image: "img-name-2:latest", | 							Image:   "img-name-2:latest", | ||||||
| 							Hash:  1002, | 							ImageID: "img-id-2", | ||||||
| 							State: "exited", | 							Hash:    1002, | ||||||
|  | 							State:   "exited", | ||||||
| 						}, | 						}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
| @@ -456,32 +460,36 @@ func TestGetPods(t *testing.T) { | |||||||
| 					Namespace: "default", | 					Namespace: "default", | ||||||
| 					Containers: []*kubecontainer.Container{ | 					Containers: []*kubecontainer.Container{ | ||||||
| 						{ | 						{ | ||||||
| 							ID:    kubecontainer.BuildContainerID("rkt", "uuid-4003:app-11"), | 							ID:      kubecontainer.BuildContainerID("rkt", "uuid-4003:app-11"), | ||||||
| 							Name:  "app-11", | 							Name:    "app-11", | ||||||
| 							Image: "img-name-11:latest", | 							Image:   "img-name-11:latest", | ||||||
| 							Hash:  10011, | 							ImageID: "img-id-11", | ||||||
| 							State: "exited", | 							Hash:    10011, | ||||||
|  | 							State:   "exited", | ||||||
| 						}, | 						}, | ||||||
| 						{ | 						{ | ||||||
| 							ID:    kubecontainer.BuildContainerID("rkt", "uuid-4003:app-22"), | 							ID:      kubecontainer.BuildContainerID("rkt", "uuid-4003:app-22"), | ||||||
| 							Name:  "app-22", | 							Name:    "app-22", | ||||||
| 							Image: "img-name-22:latest", | 							Image:   "img-name-22:latest", | ||||||
| 							Hash:  10022, | 							ImageID: "img-id-22", | ||||||
| 							State: "exited", | 							Hash:    10022, | ||||||
|  | 							State:   "exited", | ||||||
| 						}, | 						}, | ||||||
| 						{ | 						{ | ||||||
| 							ID:    kubecontainer.BuildContainerID("rkt", "uuid-4004:app-11"), | 							ID:      kubecontainer.BuildContainerID("rkt", "uuid-4004:app-11"), | ||||||
| 							Name:  "app-11", | 							Name:    "app-11", | ||||||
| 							Image: "img-name-11:latest", | 							Image:   "img-name-11:latest", | ||||||
| 							Hash:  10011, | 							ImageID: "img-id-11", | ||||||
| 							State: "running", | 							Hash:    10011, | ||||||
|  | 							State:   "running", | ||||||
| 						}, | 						}, | ||||||
| 						{ | 						{ | ||||||
| 							ID:    kubecontainer.BuildContainerID("rkt", "uuid-4004:app-22"), | 							ID:      kubecontainer.BuildContainerID("rkt", "uuid-4004:app-22"), | ||||||
| 							Name:  "app-22", | 							Name:    "app-22", | ||||||
| 							Image: "img-name-22:latest", | 							Image:   "img-name-22:latest", | ||||||
| 							Hash:  10022, | 							ImageID: "img-id-22", | ||||||
| 							State: "running", | 							Hash:    10022, | ||||||
|  | 							State:   "running", | ||||||
| 						}, | 						}, | ||||||
| 					}, | 					}, | ||||||
| 				}, | 				}, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user