rkt: Implement pod FinishedAt
				
					
				
			This is implemented via touching a file on stop as a hook in the systemd unit. The ctime of this file is then used to get the `finishedAt` time in the future. In addition, this changes the `startedAt` and `createdAt` to use the api server's results rather than the annotations it previously used. It's possible we might want to move this into the api in the future. Fixes #23887
This commit is contained in:
		@@ -25,6 +25,7 @@ import (
 | 
			
		||||
	"k8s.io/kubernetes/pkg/client/record"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/kubelet/util/format"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/runtime"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/types"
 | 
			
		||||
	hashutil "k8s.io/kubernetes/pkg/util/hash"
 | 
			
		||||
	"k8s.io/kubernetes/third_party/golang/expansion"
 | 
			
		||||
 | 
			
		||||
@@ -42,6 +43,7 @@ type RuntimeHelper interface {
 | 
			
		||||
	GenerateRunContainerOptions(pod *api.Pod, container *api.Container, podIP string) (*RunContainerOptions, error)
 | 
			
		||||
	GetClusterDNS(pod *api.Pod) (dnsServers []string, dnsSearches []string, err error)
 | 
			
		||||
	GeneratePodHostNameAndDomain(pod *api.Pod) (hostname string, hostDomain string)
 | 
			
		||||
	GetPodDir(podUID types.UID) string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ShouldContainerBeRestarted checks whether a container needs to be restarted.
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ import (
 | 
			
		||||
type OSInterface interface {
 | 
			
		||||
	Mkdir(path string, perm os.FileMode) error
 | 
			
		||||
	Symlink(oldname string, newname string) error
 | 
			
		||||
	Stat(path string) (os.FileInfo, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RealOS is used to dispatch the real system level operaitons.
 | 
			
		||||
@@ -39,3 +40,8 @@ func (RealOS) Mkdir(path string, perm os.FileMode) error {
 | 
			
		||||
func (RealOS) Symlink(oldname string, newname string) error {
 | 
			
		||||
	return os.Symlink(oldname, newname)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stat will call os.Stat to get the FileInfo for a given path
 | 
			
		||||
func (RealOS) Stat(path string) (os.FileInfo, error) {
 | 
			
		||||
	return os.Stat(path)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,12 +17,17 @@ limitations under the License.
 | 
			
		||||
package testing
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FakeOS mocks out certain OS calls to avoid perturbing the filesystem
 | 
			
		||||
// on the test machine.
 | 
			
		||||
type FakeOS struct{}
 | 
			
		||||
// If a member of the form `*Fn` is set, that function will be called in place
 | 
			
		||||
// of the real call.
 | 
			
		||||
type FakeOS struct {
 | 
			
		||||
	StatFn func(string) (os.FileInfo, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Mkdir is a fake call that just returns nil.
 | 
			
		||||
func (FakeOS) Mkdir(path string, perm os.FileMode) error {
 | 
			
		||||
@@ -33,3 +38,11 @@ func (FakeOS) Mkdir(path string, perm os.FileMode) error {
 | 
			
		||||
func (FakeOS) Symlink(oldname string, newname string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stat is a fake that returns an error
 | 
			
		||||
func (f FakeOS) Stat(path string) (os.FileInfo, error) {
 | 
			
		||||
	if f.StatFn != nil {
 | 
			
		||||
		return f.StatFn(path)
 | 
			
		||||
	}
 | 
			
		||||
	return nil, errors.New("unimplemented testing mock")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -93,6 +93,10 @@ func (f *fakeRuntimeHelper) GeneratePodHostNameAndDomain(pod *api.Pod) (string,
 | 
			
		||||
	return "", ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *fakeRuntimeHelper) GetPodDir(types.UID) string {
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createTestDockerManager(fakeHTTPClient *fakeHTTP, fakeDocker *FakeDockerClient) (*DockerManager, *FakeDockerClient) {
 | 
			
		||||
	if fakeHTTPClient == nil {
 | 
			
		||||
		fakeHTTPClient = &fakeHTTP{}
 | 
			
		||||
 
 | 
			
		||||
@@ -72,6 +72,7 @@ import (
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/atomic"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/bandwidth"
 | 
			
		||||
	utilerrors "k8s.io/kubernetes/pkg/util/errors"
 | 
			
		||||
	utilexec "k8s.io/kubernetes/pkg/util/exec"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/flowcontrol"
 | 
			
		||||
	kubeio "k8s.io/kubernetes/pkg/util/io"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/mount"
 | 
			
		||||
@@ -426,6 +427,8 @@ func NewMainKubelet(
 | 
			
		||||
			klet.livenessManager,
 | 
			
		||||
			klet.volumeManager,
 | 
			
		||||
			klet.httpClient,
 | 
			
		||||
			utilexec.New(),
 | 
			
		||||
			kubecontainer.RealOS{},
 | 
			
		||||
			imageBackOff,
 | 
			
		||||
			serializeImagePulls,
 | 
			
		||||
		)
 | 
			
		||||
@@ -830,8 +833,12 @@ func (kl *Kubelet) getPluginDir(pluginName string) string {
 | 
			
		||||
	return path.Join(kl.getPluginsDir(), pluginName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getPodDir returns the full path to the per-pod data directory for the
 | 
			
		||||
// GetPodDir returns the full path to the per-pod data directory for the
 | 
			
		||||
// specified pod. This directory may not exist if the pod does not exist.
 | 
			
		||||
func (kl *Kubelet) GetPodDir(podUID types.UID) string {
 | 
			
		||||
	return kl.getPodDir(podUID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (kl *Kubelet) getPodDir(podUID types.UID) string {
 | 
			
		||||
	// Backwards compat.  The "old" stuff should be removed before 1.0
 | 
			
		||||
	// release.  The thinking here is this:
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@ import (
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/coreos/go-systemd/dbus"
 | 
			
		||||
	rktapi "github.com/coreos/rkt/api/v1alpha"
 | 
			
		||||
@@ -166,3 +167,7 @@ func (f *fakeRuntimeHelper) GetClusterDNS(pod *api.Pod) ([]string, []string, err
 | 
			
		||||
func (f *fakeRuntimeHelper) GeneratePodHostNameAndDomain(pod *api.Pod) (string, string) {
 | 
			
		||||
	return f.hostName, f.hostDomain
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *fakeRuntimeHelper) GetPodDir(podUID types.UID) string {
 | 
			
		||||
	return "/poddir/" + string(podUID)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										109
									
								
								pkg/kubelet/rkt/mock_os/mockfileinfo.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								pkg/kubelet/rkt/mock_os/mockfileinfo.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,109 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2016 The Kubernetes Authors All rights reserved.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// Generated via: mockgen os FileInfo
 | 
			
		||||
// Edited to include required boilerplate
 | 
			
		||||
// Source: os (interfaces: FileInfo)
 | 
			
		||||
 | 
			
		||||
package mock_os
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	os "os"
 | 
			
		||||
	time "time"
 | 
			
		||||
 | 
			
		||||
	gomock "github.com/golang/mock/gomock"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Mock of FileInfo interface
 | 
			
		||||
type MockFileInfo struct {
 | 
			
		||||
	ctrl     *gomock.Controller
 | 
			
		||||
	recorder *_MockFileInfoRecorder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Recorder for MockFileInfo (not exported)
 | 
			
		||||
type _MockFileInfoRecorder struct {
 | 
			
		||||
	mock *MockFileInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewMockFileInfo(ctrl *gomock.Controller) *MockFileInfo {
 | 
			
		||||
	mock := &MockFileInfo{ctrl: ctrl}
 | 
			
		||||
	mock.recorder = &_MockFileInfoRecorder{mock}
 | 
			
		||||
	return mock
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (_m *MockFileInfo) EXPECT() *_MockFileInfoRecorder {
 | 
			
		||||
	return _m.recorder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (_m *MockFileInfo) IsDir() bool {
 | 
			
		||||
	ret := _m.ctrl.Call(_m, "IsDir")
 | 
			
		||||
	ret0, _ := ret[0].(bool)
 | 
			
		||||
	return ret0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (_mr *_MockFileInfoRecorder) IsDir() *gomock.Call {
 | 
			
		||||
	return _mr.mock.ctrl.RecordCall(_mr.mock, "IsDir")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (_m *MockFileInfo) ModTime() time.Time {
 | 
			
		||||
	ret := _m.ctrl.Call(_m, "ModTime")
 | 
			
		||||
	ret0, _ := ret[0].(time.Time)
 | 
			
		||||
	return ret0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (_mr *_MockFileInfoRecorder) ModTime() *gomock.Call {
 | 
			
		||||
	return _mr.mock.ctrl.RecordCall(_mr.mock, "ModTime")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (_m *MockFileInfo) Mode() os.FileMode {
 | 
			
		||||
	ret := _m.ctrl.Call(_m, "Mode")
 | 
			
		||||
	ret0, _ := ret[0].(os.FileMode)
 | 
			
		||||
	return ret0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (_mr *_MockFileInfoRecorder) Mode() *gomock.Call {
 | 
			
		||||
	return _mr.mock.ctrl.RecordCall(_mr.mock, "Mode")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (_m *MockFileInfo) Name() string {
 | 
			
		||||
	ret := _m.ctrl.Call(_m, "Name")
 | 
			
		||||
	ret0, _ := ret[0].(string)
 | 
			
		||||
	return ret0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (_mr *_MockFileInfoRecorder) Name() *gomock.Call {
 | 
			
		||||
	return _mr.mock.ctrl.RecordCall(_mr.mock, "Name")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (_m *MockFileInfo) Size() int64 {
 | 
			
		||||
	ret := _m.ctrl.Call(_m, "Size")
 | 
			
		||||
	ret0, _ := ret[0].(int64)
 | 
			
		||||
	return ret0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (_mr *_MockFileInfoRecorder) Size() *gomock.Call {
 | 
			
		||||
	return _mr.mock.ctrl.RecordCall(_mr.mock, "Size")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (_m *MockFileInfo) Sys() interface{} {
 | 
			
		||||
	ret := _m.ctrl.Call(_m, "Sys")
 | 
			
		||||
	ret0, _ := ret[0].(interface{})
 | 
			
		||||
	return ret0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (_mr *_MockFileInfoRecorder) Sys() *gomock.Call {
 | 
			
		||||
	return _mr.mock.ctrl.RecordCall(_mr.mock, "Sys")
 | 
			
		||||
}
 | 
			
		||||
@@ -25,6 +25,7 @@ import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
@@ -81,8 +82,6 @@ const (
 | 
			
		||||
	k8sRktUIDAnno                    = "rkt.kubernetes.io/uid"
 | 
			
		||||
	k8sRktNameAnno                   = "rkt.kubernetes.io/name"
 | 
			
		||||
	k8sRktNamespaceAnno              = "rkt.kubernetes.io/namespace"
 | 
			
		||||
	//TODO: remove the creation time annotation once this is closed: https://github.com/coreos/rkt/issues/1789
 | 
			
		||||
	k8sRktCreationTimeAnno           = "rkt.kubernetes.io/created"
 | 
			
		||||
	k8sRktContainerHashAnno          = "rkt.kubernetes.io/container-hash"
 | 
			
		||||
	k8sRktRestartCountAnno           = "rkt.kubernetes.io/restart-count"
 | 
			
		||||
	k8sRktTerminationMessagePathAnno = "rkt.kubernetes.io/termination-message-path"
 | 
			
		||||
@@ -128,6 +127,11 @@ type Runtime struct {
 | 
			
		||||
	volumeGetter        volumeGetter
 | 
			
		||||
	imagePuller         kubecontainer.ImagePuller
 | 
			
		||||
	runner              kubecontainer.HandlerRunner
 | 
			
		||||
	execer              utilexec.Interface
 | 
			
		||||
	os                  kubecontainer.OSInterface
 | 
			
		||||
 | 
			
		||||
	// used for a systemd Exec, which requires the full path.
 | 
			
		||||
	touchPath string
 | 
			
		||||
 | 
			
		||||
	versions versions
 | 
			
		||||
}
 | 
			
		||||
@@ -151,6 +155,8 @@ func New(
 | 
			
		||||
	livenessManager proberesults.Manager,
 | 
			
		||||
	volumeGetter volumeGetter,
 | 
			
		||||
	httpClient kubetypes.HttpGetter,
 | 
			
		||||
	execer utilexec.Interface,
 | 
			
		||||
	os kubecontainer.OSInterface,
 | 
			
		||||
	imageBackOff *flowcontrol.Backoff,
 | 
			
		||||
	serializeImagePulls bool,
 | 
			
		||||
) (*Runtime, error) {
 | 
			
		||||
@@ -170,12 +176,17 @@ func New(
 | 
			
		||||
	if config.Path == "" {
 | 
			
		||||
		// No default rkt path was set, so try to find one in $PATH.
 | 
			
		||||
		var err error
 | 
			
		||||
		config.Path, err = exec.LookPath("rkt")
 | 
			
		||||
		config.Path, err = execer.LookPath("rkt")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("cannot find rkt binary: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	touchPath, err := execer.LookPath("touch")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("cannot find touch binary: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rkt := &Runtime{
 | 
			
		||||
		systemd:             systemd,
 | 
			
		||||
		apisvcConn:          apisvcConn,
 | 
			
		||||
@@ -187,6 +198,8 @@ func New(
 | 
			
		||||
		recorder:            recorder,
 | 
			
		||||
		livenessManager:     livenessManager,
 | 
			
		||||
		volumeGetter:        volumeGetter,
 | 
			
		||||
		execer:              execer,
 | 
			
		||||
		touchPath:           touchPath,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rkt.config, err = rkt.getConfig(rkt.config)
 | 
			
		||||
@@ -541,7 +554,6 @@ func (r *Runtime) makePodManifest(pod *api.Pod, pullSecrets []api.Secret) (*appc
 | 
			
		||||
	manifest.Annotations.Set(*appctypes.MustACIdentifier(k8sRktUIDAnno), string(pod.UID))
 | 
			
		||||
	manifest.Annotations.Set(*appctypes.MustACIdentifier(k8sRktNameAnno), pod.Name)
 | 
			
		||||
	manifest.Annotations.Set(*appctypes.MustACIdentifier(k8sRktNamespaceAnno), pod.Namespace)
 | 
			
		||||
	manifest.Annotations.Set(*appctypes.MustACIdentifier(k8sRktCreationTimeAnno), strconv.FormatInt(time.Now().Unix(), 10))
 | 
			
		||||
	manifest.Annotations.Set(*appctypes.MustACIdentifier(k8sRktRestartCountAnno), strconv.Itoa(restartCount))
 | 
			
		||||
 | 
			
		||||
	for _, c := range pod.Spec.Containers {
 | 
			
		||||
@@ -587,6 +599,34 @@ func makeHostNetworkMount(opts *kubecontainer.RunContainerOptions) (*kubecontain
 | 
			
		||||
	return &hostsMount, &resolvMount
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// podFinishedMarkerPath returns the path to a file which should be used to
 | 
			
		||||
// indicate the pod exiting, and the time thereof.
 | 
			
		||||
// If the file at the path does not exist, the pod should not be exited. If it
 | 
			
		||||
// does exist, then the ctime of the file should indicate the time the pod
 | 
			
		||||
// exited.
 | 
			
		||||
func podFinishedMarkerPath(podDir string, rktUID string) string {
 | 
			
		||||
	return filepath.Join(podDir, "finished-"+rktUID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func podFinishedMarkCommand(touchPath, podDir, rktUID string) string {
 | 
			
		||||
	// TODO, if the path has a `'` character in it, this breaks.
 | 
			
		||||
	return touchPath + " " + podFinishedMarkerPath(podDir, rktUID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// podFinishedAt returns the time that a pod exited, or a zero time if it has
 | 
			
		||||
// not.
 | 
			
		||||
func (r *Runtime) podFinishedAt(podUID types.UID, rktUID string) time.Time {
 | 
			
		||||
	markerFile := podFinishedMarkerPath(r.runtimeHelper.GetPodDir(podUID), rktUID)
 | 
			
		||||
	stat, err := r.os.Stat(markerFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if !os.IsNotExist(err) {
 | 
			
		||||
			glog.Warningf("rkt: unexpected fs error checking pod finished marker: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		return time.Time{}
 | 
			
		||||
	}
 | 
			
		||||
	return stat.ModTime()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeContainerLogMount(opts *kubecontainer.RunContainerOptions, container *api.Container) (*kubecontainer.Mount, error) {
 | 
			
		||||
	if opts.PodContainerDir == "" || container.TerminationMessagePath == "" {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
@@ -884,8 +924,11 @@ func (r *Runtime) preparePod(pod *api.Pod, pullSecrets []api.Secret) (string, *k
 | 
			
		||||
	// TODO handle pod.Spec.HostPID
 | 
			
		||||
	// TODO handle pod.Spec.HostIPC
 | 
			
		||||
 | 
			
		||||
	// TODO per container finishedAt, not just per pod
 | 
			
		||||
	markPodFinished := podFinishedMarkCommand(r.touchPath, r.runtimeHelper.GetPodDir(pod.UID), uuid)
 | 
			
		||||
	units := []*unit.UnitOption{
 | 
			
		||||
		newUnitOption("Service", "ExecStart", runPrepared),
 | 
			
		||||
		newUnitOption("Service", "ExecStopPost", markPodFinished),
 | 
			
		||||
		// This enables graceful stop.
 | 
			
		||||
		newUnitOption("Service", "KillMode", "mixed"),
 | 
			
		||||
	}
 | 
			
		||||
@@ -1045,14 +1088,6 @@ func (r *Runtime) convertRktPod(rktpod *rktapi.Pod) (*kubecontainer.Pod, error)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, fmt.Errorf("pod is missing annotation %s", k8sRktNamespaceAnno)
 | 
			
		||||
	}
 | 
			
		||||
	podCreatedString, ok := manifest.Annotations.Get(k8sRktCreationTimeAnno)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, fmt.Errorf("pod is missing annotation %s", k8sRktCreationTimeAnno)
 | 
			
		||||
	}
 | 
			
		||||
	podCreated, err := strconv.ParseInt(podCreatedString, 10, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("couldn't parse pod creation timestamp: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kubepod := &kubecontainer.Pod{
 | 
			
		||||
		ID:        types.UID(podUID),
 | 
			
		||||
@@ -1078,8 +1113,8 @@ func (r *Runtime) convertRktPod(rktpod *rktapi.Pod) (*kubecontainer.Pod, error)
 | 
			
		||||
			// 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),
 | 
			
		||||
			Hash:    containerHash,
 | 
			
		||||
			Created: podCreated,
 | 
			
		||||
			State:   appStateToContainerState(app.State),
 | 
			
		||||
			Created: time.Unix(0, rktpod.CreatedAt).Unix(), // convert ns to s
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -1526,7 +1561,7 @@ func appStateToContainerState(state rktapi.AppState) kubecontainer.ContainerStat
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getPodInfo returns the pod manifest, creation time and restart count of the pod.
 | 
			
		||||
func getPodInfo(pod *rktapi.Pod) (podManifest *appcschema.PodManifest, creationTime time.Time, restartCount int, err error) {
 | 
			
		||||
func getPodInfo(pod *rktapi.Pod) (podManifest *appcschema.PodManifest, restartCount int, err error) {
 | 
			
		||||
	// TODO(yifan): The manifest is only used for getting the annotations.
 | 
			
		||||
	// Consider to let the server to unmarshal the annotations.
 | 
			
		||||
	var manifest appcschema.PodManifest
 | 
			
		||||
@@ -1534,16 +1569,6 @@ func getPodInfo(pod *rktapi.Pod) (podManifest *appcschema.PodManifest, creationT
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	creationTimeStr, ok := manifest.Annotations.Get(k8sRktCreationTimeAnno)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		err = fmt.Errorf("no creation timestamp in pod manifest")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	unixSec, err := strconv.ParseInt(creationTimeStr, 10, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if countString, ok := manifest.Annotations.Get(k8sRktRestartCountAnno); ok {
 | 
			
		||||
		restartCount, err = strconv.Atoi(countString)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
@@ -1551,11 +1576,11 @@ func getPodInfo(pod *rktapi.Pod) (podManifest *appcschema.PodManifest, creationT
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &manifest, time.Unix(unixSec, 0), restartCount, nil
 | 
			
		||||
	return &manifest, restartCount, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// populateContainerStatus fills the container status according to the app's information.
 | 
			
		||||
func populateContainerStatus(pod rktapi.Pod, app rktapi.App, runtimeApp appcschema.RuntimeApp, restartCount int, creationTime time.Time) (*kubecontainer.ContainerStatus, error) {
 | 
			
		||||
func populateContainerStatus(pod rktapi.Pod, app rktapi.App, runtimeApp appcschema.RuntimeApp, restartCount int, finishedTime time.Time) (*kubecontainer.ContainerStatus, error) {
 | 
			
		||||
	hashStr, ok := runtimeApp.Annotations.Get(k8sRktContainerHashAnno)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, fmt.Errorf("No container hash in pod manifest")
 | 
			
		||||
@@ -1584,13 +1609,16 @@ func populateContainerStatus(pod rktapi.Pod, app rktapi.App, runtimeApp appcsche
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	createdTime := time.Unix(0, pod.CreatedAt)
 | 
			
		||||
	startedTime := time.Unix(0, pod.StartedAt)
 | 
			
		||||
 | 
			
		||||
	return &kubecontainer.ContainerStatus{
 | 
			
		||||
		ID:         buildContainerID(&containerID{uuid: pod.Id, appName: app.Name}),
 | 
			
		||||
		Name:       app.Name,
 | 
			
		||||
		State:      appStateToContainerState(app.State),
 | 
			
		||||
		// TODO(yifan): Use the creation/start/finished timestamp when it's implemented.
 | 
			
		||||
		CreatedAt: creationTime,
 | 
			
		||||
		StartedAt: creationTime,
 | 
			
		||||
		CreatedAt:  createdTime,
 | 
			
		||||
		StartedAt:  startedTime,
 | 
			
		||||
		FinishedAt: finishedTime,
 | 
			
		||||
		ExitCode:   int(app.ExitCode),
 | 
			
		||||
		// 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),
 | 
			
		||||
@@ -1605,6 +1633,13 @@ func populateContainerStatus(pod rktapi.Pod, app rktapi.App, runtimeApp appcsche
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetPodStatus returns the status for a pod specified by a given UID, name,
 | 
			
		||||
// and namespace.  It will attempt to find pod's information via a request to
 | 
			
		||||
// the rkt api server.
 | 
			
		||||
// An error will be returned if the api server returns an error. If the api
 | 
			
		||||
// server doesn't error, but doesn't provide meaningful information about the
 | 
			
		||||
// pod, a status with no information (other than the passed in arguments) is
 | 
			
		||||
// returned anyways.
 | 
			
		||||
func (r *Runtime) GetPodStatus(uid types.UID, name, namespace string) (*kubecontainer.PodStatus, error) {
 | 
			
		||||
	podStatus := &kubecontainer.PodStatus{
 | 
			
		||||
		ID:        uid,
 | 
			
		||||
@@ -1626,7 +1661,7 @@ func (r *Runtime) GetPodStatus(uid types.UID, name, namespace string) (*kubecont
 | 
			
		||||
	// In this loop, we group all containers from all pods together,
 | 
			
		||||
	// also we try to find the latest pod, so we can fill other info of the pod below.
 | 
			
		||||
	for _, pod := range listResp.Pods {
 | 
			
		||||
		manifest, creationTime, restartCount, err := getPodInfo(pod)
 | 
			
		||||
		manifest, restartCount, err := getPodInfo(pod)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			glog.Warningf("rkt: Couldn't get necessary info from the rkt pod, (uuid %q): %v", pod.Id, err)
 | 
			
		||||
			continue
 | 
			
		||||
@@ -1637,11 +1672,10 @@ func (r *Runtime) GetPodStatus(uid types.UID, name, namespace string) (*kubecont
 | 
			
		||||
			latestRestartCount = restartCount
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		finishedTime := r.podFinishedAt(uid, pod.Id)
 | 
			
		||||
		for i, app := range pod.Apps {
 | 
			
		||||
			// The order of the apps is determined by the rkt pod manifest.
 | 
			
		||||
			// TODO(yifan): Save creationTime, restartCount in each app's annotation,
 | 
			
		||||
			// so we don't need to pass them.
 | 
			
		||||
			cs, err := populateContainerStatus(*pod, *app, manifest.Apps[i], restartCount, creationTime)
 | 
			
		||||
			cs, err := populateContainerStatus(*pod, *app, manifest.Apps[i], restartCount, finishedTime)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				glog.Warningf("rkt: Failed to populate container status(uuid %q, app %q): %v", pod.Id, app.Name, err)
 | 
			
		||||
				continue
 | 
			
		||||
 
 | 
			
		||||
@@ -27,11 +27,14 @@ import (
 | 
			
		||||
	appcschema "github.com/appc/spec/schema"
 | 
			
		||||
	appctypes "github.com/appc/spec/schema/types"
 | 
			
		||||
	rktapi "github.com/coreos/rkt/api/v1alpha"
 | 
			
		||||
	"github.com/golang/mock/gomock"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api/resource"
 | 
			
		||||
	kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
 | 
			
		||||
	containertesting "k8s.io/kubernetes/pkg/kubelet/container/testing"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/kubelet/lifecycle"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/kubelet/rkt/mock_os"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/util/errors"
 | 
			
		||||
	utiltesting "k8s.io/kubernetes/pkg/util/testing"
 | 
			
		||||
)
 | 
			
		||||
@@ -62,9 +65,10 @@ func mustRktHash(hash string) *appctypes.Hash {
 | 
			
		||||
 | 
			
		||||
func makeRktPod(rktPodState rktapi.PodState,
 | 
			
		||||
	rktPodID, podUID, podName, podNamespace,
 | 
			
		||||
	podIP, podCreationTs, podRestartCount string,
 | 
			
		||||
	appNames, imgIDs, imgNames, containerHashes []string,
 | 
			
		||||
	appStates []rktapi.AppState, exitcodes []int32) *rktapi.Pod {
 | 
			
		||||
	podIP string, podCreatedAt, podStartedAt int64,
 | 
			
		||||
	podRestartCount string, appNames, imgIDs, imgNames,
 | 
			
		||||
	containerHashes []string, appStates []rktapi.AppState,
 | 
			
		||||
	exitcodes []int32) *rktapi.Pod {
 | 
			
		||||
 | 
			
		||||
	podManifest := &appcschema.PodManifest{
 | 
			
		||||
		ACKind:    appcschema.PodManifestKind,
 | 
			
		||||
@@ -86,10 +90,6 @@ func makeRktPod(rktPodState rktapi.PodState,
 | 
			
		||||
				Name:  *appctypes.MustACIdentifier(k8sRktNamespaceAnno),
 | 
			
		||||
				Value: podNamespace,
 | 
			
		||||
			},
 | 
			
		||||
			appctypes.Annotation{
 | 
			
		||||
				Name:  *appctypes.MustACIdentifier(k8sRktCreationTimeAnno),
 | 
			
		||||
				Value: podCreationTs,
 | 
			
		||||
			},
 | 
			
		||||
			appctypes.Annotation{
 | 
			
		||||
				Name:  *appctypes.MustACIdentifier(k8sRktRestartCountAnno),
 | 
			
		||||
				Value: podRestartCount,
 | 
			
		||||
@@ -148,6 +148,8 @@ func makeRktPod(rktPodState rktapi.PodState,
 | 
			
		||||
		Networks:  []*rktapi.Network{{Name: defaultNetworkName, Ipv4: podIP}},
 | 
			
		||||
		Apps:      apps,
 | 
			
		||||
		Manifest:  mustMarshalPodManifest(podManifest),
 | 
			
		||||
		StartedAt: podStartedAt,
 | 
			
		||||
		CreatedAt: podCreatedAt,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -346,6 +348,10 @@ func TestGetPods(t *testing.T) {
 | 
			
		||||
	fs := newFakeSystemd()
 | 
			
		||||
	r := &Runtime{apisvc: fr, systemd: fs}
 | 
			
		||||
 | 
			
		||||
	ns := func(seconds int64) int64 {
 | 
			
		||||
		return seconds * 1e9
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		pods   []*rktapi.Pod
 | 
			
		||||
		result []*kubecontainer.Pod
 | 
			
		||||
@@ -357,7 +363,7 @@ func TestGetPods(t *testing.T) {
 | 
			
		||||
			[]*rktapi.Pod{
 | 
			
		||||
				makeRktPod(rktapi.PodState_POD_STATE_RUNNING,
 | 
			
		||||
					"uuid-4002", "42", "guestbook", "default",
 | 
			
		||||
					"10.10.10.42", "100000", "7",
 | 
			
		||||
					"10.10.10.42", ns(10), ns(10), "7",
 | 
			
		||||
					[]string{"app-1", "app-2"},
 | 
			
		||||
					[]string{"img-id-1", "img-id-2"},
 | 
			
		||||
					[]string{"img-name-1", "img-name-2"},
 | 
			
		||||
@@ -377,7 +383,7 @@ func TestGetPods(t *testing.T) {
 | 
			
		||||
							Name:    "app-1",
 | 
			
		||||
							Image:   "img-name-1:latest",
 | 
			
		||||
							Hash:    1001,
 | 
			
		||||
							Created: 100000,
 | 
			
		||||
							Created: 10,
 | 
			
		||||
							State:   "running",
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
@@ -385,7 +391,7 @@ func TestGetPods(t *testing.T) {
 | 
			
		||||
							Name:    "app-2",
 | 
			
		||||
							Image:   "img-name-2:latest",
 | 
			
		||||
							Hash:    1002,
 | 
			
		||||
							Created: 100000,
 | 
			
		||||
							Created: 10,
 | 
			
		||||
							State:   "exited",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
@@ -397,7 +403,7 @@ func TestGetPods(t *testing.T) {
 | 
			
		||||
			[]*rktapi.Pod{
 | 
			
		||||
				makeRktPod(rktapi.PodState_POD_STATE_RUNNING,
 | 
			
		||||
					"uuid-4002", "42", "guestbook", "default",
 | 
			
		||||
					"10.10.10.42", "100000", "7",
 | 
			
		||||
					"10.10.10.42", ns(10), ns(20), "7",
 | 
			
		||||
					[]string{"app-1", "app-2"},
 | 
			
		||||
					[]string{"img-id-1", "img-id-2"},
 | 
			
		||||
					[]string{"img-name-1", "img-name-2"},
 | 
			
		||||
@@ -407,7 +413,7 @@ func TestGetPods(t *testing.T) {
 | 
			
		||||
				),
 | 
			
		||||
				makeRktPod(rktapi.PodState_POD_STATE_EXITED,
 | 
			
		||||
					"uuid-4003", "43", "guestbook", "default",
 | 
			
		||||
					"10.10.10.43", "90000", "7",
 | 
			
		||||
					"10.10.10.43", ns(30), ns(40), "7",
 | 
			
		||||
					[]string{"app-11", "app-22"},
 | 
			
		||||
					[]string{"img-id-11", "img-id-22"},
 | 
			
		||||
					[]string{"img-name-11", "img-name-22"},
 | 
			
		||||
@@ -417,7 +423,7 @@ func TestGetPods(t *testing.T) {
 | 
			
		||||
				),
 | 
			
		||||
				makeRktPod(rktapi.PodState_POD_STATE_EXITED,
 | 
			
		||||
					"uuid-4004", "43", "guestbook", "default",
 | 
			
		||||
					"10.10.10.44", "100000", "8",
 | 
			
		||||
					"10.10.10.44", ns(50), ns(60), "8",
 | 
			
		||||
					[]string{"app-11", "app-22"},
 | 
			
		||||
					[]string{"img-id-11", "img-id-22"},
 | 
			
		||||
					[]string{"img-name-11", "img-name-22"},
 | 
			
		||||
@@ -437,7 +443,7 @@ func TestGetPods(t *testing.T) {
 | 
			
		||||
							Name:    "app-1",
 | 
			
		||||
							Image:   "img-name-1:latest",
 | 
			
		||||
							Hash:    1001,
 | 
			
		||||
							Created: 100000,
 | 
			
		||||
							Created: 10,
 | 
			
		||||
							State:   "running",
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
@@ -445,7 +451,7 @@ func TestGetPods(t *testing.T) {
 | 
			
		||||
							Name:    "app-2",
 | 
			
		||||
							Image:   "img-name-2:latest",
 | 
			
		||||
							Hash:    1002,
 | 
			
		||||
							Created: 100000,
 | 
			
		||||
							Created: 10,
 | 
			
		||||
							State:   "exited",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
@@ -460,7 +466,7 @@ func TestGetPods(t *testing.T) {
 | 
			
		||||
							Name:    "app-11",
 | 
			
		||||
							Image:   "img-name-11:latest",
 | 
			
		||||
							Hash:    10011,
 | 
			
		||||
							Created: 90000,
 | 
			
		||||
							Created: 30,
 | 
			
		||||
							State:   "exited",
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
@@ -468,7 +474,7 @@ func TestGetPods(t *testing.T) {
 | 
			
		||||
							Name:    "app-22",
 | 
			
		||||
							Image:   "img-name-22:latest",
 | 
			
		||||
							Hash:    10022,
 | 
			
		||||
							Created: 90000,
 | 
			
		||||
							Created: 30,
 | 
			
		||||
							State:   "exited",
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
@@ -476,7 +482,7 @@ func TestGetPods(t *testing.T) {
 | 
			
		||||
							Name:    "app-11",
 | 
			
		||||
							Image:   "img-name-11:latest",
 | 
			
		||||
							Hash:    10011,
 | 
			
		||||
							Created: 100000,
 | 
			
		||||
							Created: 50,
 | 
			
		||||
							State:   "running",
 | 
			
		||||
						},
 | 
			
		||||
						{
 | 
			
		||||
@@ -484,7 +490,7 @@ func TestGetPods(t *testing.T) {
 | 
			
		||||
							Name:    "app-22",
 | 
			
		||||
							Image:   "img-name-22:latest",
 | 
			
		||||
							Hash:    10022,
 | 
			
		||||
							Created: 100000,
 | 
			
		||||
							Created: 50,
 | 
			
		||||
							State:   "running",
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
@@ -557,7 +563,18 @@ func TestGetPodsFilters(t *testing.T) {
 | 
			
		||||
func TestGetPodStatus(t *testing.T) {
 | 
			
		||||
	fr := newFakeRktInterface()
 | 
			
		||||
	fs := newFakeSystemd()
 | 
			
		||||
	r := &Runtime{apisvc: fr, systemd: fs}
 | 
			
		||||
	fos := &containertesting.FakeOS{}
 | 
			
		||||
	frh := &fakeRuntimeHelper{}
 | 
			
		||||
	r := &Runtime{
 | 
			
		||||
		apisvc:        fr,
 | 
			
		||||
		systemd:       fs,
 | 
			
		||||
		runtimeHelper: frh,
 | 
			
		||||
		os:            fos,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ns := func(seconds int64) int64 {
 | 
			
		||||
		return seconds * 1e9
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		pods   []*rktapi.Pod
 | 
			
		||||
@@ -573,7 +590,7 @@ func TestGetPodStatus(t *testing.T) {
 | 
			
		||||
			[]*rktapi.Pod{
 | 
			
		||||
				makeRktPod(rktapi.PodState_POD_STATE_RUNNING,
 | 
			
		||||
					"uuid-4002", "42", "guestbook", "default",
 | 
			
		||||
					"10.10.10.42", "100000", "7",
 | 
			
		||||
					"10.10.10.42", ns(10), ns(20), "7",
 | 
			
		||||
					[]string{"app-1", "app-2"},
 | 
			
		||||
					[]string{"img-id-1", "img-id-2"},
 | 
			
		||||
					[]string{"img-name-1", "img-name-2"},
 | 
			
		||||
@@ -592,8 +609,9 @@ func TestGetPodStatus(t *testing.T) {
 | 
			
		||||
						ID:           kubecontainer.BuildContainerID("rkt", "uuid-4002:app-1"),
 | 
			
		||||
						Name:         "app-1",
 | 
			
		||||
						State:        kubecontainer.ContainerStateRunning,
 | 
			
		||||
						CreatedAt:    time.Unix(100000, 0),
 | 
			
		||||
						StartedAt:    time.Unix(100000, 0),
 | 
			
		||||
						CreatedAt:    time.Unix(10, 0),
 | 
			
		||||
						StartedAt:    time.Unix(20, 0),
 | 
			
		||||
						FinishedAt:   time.Unix(0, 30),
 | 
			
		||||
						Image:        "img-name-1:latest",
 | 
			
		||||
						ImageID:      "rkt://img-id-1",
 | 
			
		||||
						Hash:         1001,
 | 
			
		||||
@@ -603,8 +621,9 @@ func TestGetPodStatus(t *testing.T) {
 | 
			
		||||
						ID:           kubecontainer.BuildContainerID("rkt", "uuid-4002:app-2"),
 | 
			
		||||
						Name:         "app-2",
 | 
			
		||||
						State:        kubecontainer.ContainerStateExited,
 | 
			
		||||
						CreatedAt:    time.Unix(100000, 0),
 | 
			
		||||
						StartedAt:    time.Unix(100000, 0),
 | 
			
		||||
						CreatedAt:    time.Unix(10, 0),
 | 
			
		||||
						StartedAt:    time.Unix(20, 0),
 | 
			
		||||
						FinishedAt:   time.Unix(0, 30),
 | 
			
		||||
						Image:        "img-name-2:latest",
 | 
			
		||||
						ImageID:      "rkt://img-id-2",
 | 
			
		||||
						Hash:         1002,
 | 
			
		||||
@@ -619,7 +638,7 @@ func TestGetPodStatus(t *testing.T) {
 | 
			
		||||
			[]*rktapi.Pod{
 | 
			
		||||
				makeRktPod(rktapi.PodState_POD_STATE_EXITED,
 | 
			
		||||
					"uuid-4002", "42", "guestbook", "default",
 | 
			
		||||
					"10.10.10.42", "90000", "7",
 | 
			
		||||
					"10.10.10.42", ns(10), ns(20), "7",
 | 
			
		||||
					[]string{"app-1", "app-2"},
 | 
			
		||||
					[]string{"img-id-1", "img-id-2"},
 | 
			
		||||
					[]string{"img-name-1", "img-name-2"},
 | 
			
		||||
@@ -629,7 +648,7 @@ func TestGetPodStatus(t *testing.T) {
 | 
			
		||||
				),
 | 
			
		||||
				makeRktPod(rktapi.PodState_POD_STATE_RUNNING, // The latest pod is running.
 | 
			
		||||
					"uuid-4003", "42", "guestbook", "default",
 | 
			
		||||
					"10.10.10.42", "100000", "10",
 | 
			
		||||
					"10.10.10.42", ns(10), ns(20), "10",
 | 
			
		||||
					[]string{"app-1", "app-2"},
 | 
			
		||||
					[]string{"img-id-1", "img-id-2"},
 | 
			
		||||
					[]string{"img-name-1", "img-name-2"},
 | 
			
		||||
@@ -649,8 +668,9 @@ func TestGetPodStatus(t *testing.T) {
 | 
			
		||||
						ID:           kubecontainer.BuildContainerID("rkt", "uuid-4002:app-1"),
 | 
			
		||||
						Name:         "app-1",
 | 
			
		||||
						State:        kubecontainer.ContainerStateRunning,
 | 
			
		||||
						CreatedAt:    time.Unix(90000, 0),
 | 
			
		||||
						StartedAt:    time.Unix(90000, 0),
 | 
			
		||||
						CreatedAt:    time.Unix(10, 0),
 | 
			
		||||
						StartedAt:    time.Unix(20, 0),
 | 
			
		||||
						FinishedAt:   time.Unix(0, 30),
 | 
			
		||||
						Image:        "img-name-1:latest",
 | 
			
		||||
						ImageID:      "rkt://img-id-1",
 | 
			
		||||
						Hash:         1001,
 | 
			
		||||
@@ -660,8 +680,9 @@ func TestGetPodStatus(t *testing.T) {
 | 
			
		||||
						ID:           kubecontainer.BuildContainerID("rkt", "uuid-4002:app-2"),
 | 
			
		||||
						Name:         "app-2",
 | 
			
		||||
						State:        kubecontainer.ContainerStateExited,
 | 
			
		||||
						CreatedAt:    time.Unix(90000, 0),
 | 
			
		||||
						StartedAt:    time.Unix(90000, 0),
 | 
			
		||||
						CreatedAt:    time.Unix(10, 0),
 | 
			
		||||
						StartedAt:    time.Unix(20, 0),
 | 
			
		||||
						FinishedAt:   time.Unix(0, 30),
 | 
			
		||||
						Image:        "img-name-2:latest",
 | 
			
		||||
						ImageID:      "rkt://img-id-2",
 | 
			
		||||
						Hash:         1002,
 | 
			
		||||
@@ -672,8 +693,9 @@ func TestGetPodStatus(t *testing.T) {
 | 
			
		||||
						ID:           kubecontainer.BuildContainerID("rkt", "uuid-4003:app-1"),
 | 
			
		||||
						Name:         "app-1",
 | 
			
		||||
						State:        kubecontainer.ContainerStateRunning,
 | 
			
		||||
						CreatedAt:    time.Unix(100000, 0),
 | 
			
		||||
						StartedAt:    time.Unix(100000, 0),
 | 
			
		||||
						CreatedAt:    time.Unix(10, 0),
 | 
			
		||||
						StartedAt:    time.Unix(20, 0),
 | 
			
		||||
						FinishedAt:   time.Unix(0, 30),
 | 
			
		||||
						Image:        "img-name-1:latest",
 | 
			
		||||
						ImageID:      "rkt://img-id-1",
 | 
			
		||||
						Hash:         1001,
 | 
			
		||||
@@ -683,8 +705,9 @@ func TestGetPodStatus(t *testing.T) {
 | 
			
		||||
						ID:           kubecontainer.BuildContainerID("rkt", "uuid-4003:app-2"),
 | 
			
		||||
						Name:         "app-2",
 | 
			
		||||
						State:        kubecontainer.ContainerStateExited,
 | 
			
		||||
						CreatedAt:    time.Unix(100000, 0),
 | 
			
		||||
						StartedAt:    time.Unix(100000, 0),
 | 
			
		||||
						CreatedAt:    time.Unix(10, 0),
 | 
			
		||||
						StartedAt:    time.Unix(20, 0),
 | 
			
		||||
						FinishedAt:   time.Unix(0, 30),
 | 
			
		||||
						Image:        "img-name-2:latest",
 | 
			
		||||
						ImageID:      "rkt://img-id-2",
 | 
			
		||||
						Hash:         1002,
 | 
			
		||||
@@ -697,10 +720,28 @@ func TestGetPodStatus(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctrl := gomock.NewController(t)
 | 
			
		||||
	defer ctrl.Finish()
 | 
			
		||||
 | 
			
		||||
	for i, tt := range tests {
 | 
			
		||||
		testCaseHint := fmt.Sprintf("test case #%d", i)
 | 
			
		||||
		fr.pods = tt.pods
 | 
			
		||||
 | 
			
		||||
		podTimes := map[string]time.Time{}
 | 
			
		||||
		for _, pod := range tt.pods {
 | 
			
		||||
			podTimes[podFinishedMarkerPath(r.runtimeHelper.GetPodDir(tt.result.ID), pod.Id)] = tt.result.ContainerStatuses[0].FinishedAt
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		r.os.(*containertesting.FakeOS).StatFn = func(name string) (os.FileInfo, error) {
 | 
			
		||||
			podTime, ok := podTimes[name]
 | 
			
		||||
			if !ok {
 | 
			
		||||
				t.Errorf("osStat called with %v, but only knew about %#v", name, podTimes)
 | 
			
		||||
			}
 | 
			
		||||
			mockFI := mock_os.NewMockFileInfo(ctrl)
 | 
			
		||||
			mockFI.EXPECT().ModTime().Return(podTime)
 | 
			
		||||
			return mockFI, nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		status, err := r.GetPodStatus("42", "guestbook", "default")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("test case #%d: unexpected error: %v", i, err)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user