// Get returns a streamer resource with the contents of the build log func (r *REST) Get(ctx kapi.Context, name string, opts runtime.Object) (runtime.Object, error) { buildLogOpts, ok := opts.(*api.BuildLogOptions) if !ok { return nil, errors.NewBadRequest("did not get an expected options.") } obj, err := r.Getter.Get(ctx, name) if err != nil { return nil, err } build := obj.(*api.Build) switch build.Status.Phase { // Build has not launched, wait til it runs case api.BuildPhaseNew, api.BuildPhasePending: if buildLogOpts.NoWait { glog.V(4).Infof("Build %s/%s is in %s state. No logs to retrieve yet.", build.Namespace, name, build.Status.Phase) // return empty content if not waiting for build return &genericrest.LocationStreamer{}, nil } glog.V(4).Infof("Build %s/%s is in %s state, waiting for Build to start", build.Namespace, name, build.Status.Phase) latest, ok, err := registry.WaitForRunningBuild(r.Watcher, ctx, build, r.Timeout) if err != nil { return nil, errors.NewBadRequest(fmt.Sprintf("unable to wait for build %s to run: %v", name, err)) } switch latest.Status.Phase { case api.BuildPhaseError: return nil, errors.NewBadRequest(fmt.Sprintf("build %s encountered an error: %s", name, buildutil.NoBuildLogsMessage)) case api.BuildPhaseCancelled: return nil, errors.NewBadRequest(fmt.Sprintf("build %s was cancelled: %s", name, buildutil.NoBuildLogsMessage)) } if !ok { return nil, errors.NewTimeoutError(fmt.Sprintf("timed out waiting for build %s to start after %s", build.Name, r.Timeout), 1) } // The build was cancelled case api.BuildPhaseCancelled: return nil, errors.NewBadRequest(fmt.Sprintf("build %s was cancelled. %s", name, buildutil.NoBuildLogsMessage)) // An error occurred launching the build, return an error case api.BuildPhaseError: return nil, errors.NewBadRequest(fmt.Sprintf("build %s is in an error state. %s", name, buildutil.NoBuildLogsMessage)) } // The container should be the default build container, so setting it to blank buildPodName := buildutil.GetBuildPodName(build) logOpts := api.BuildToPodLogOptions(buildLogOpts) location, transport, err := pod.LogLocation(r.PodGetter, r.ConnectionInfo, ctx, buildPodName, logOpts) if err != nil { if errors.IsNotFound(err) { return nil, errors.NewNotFound("pod", buildPodName) } return nil, errors.NewBadRequest(err.Error()) } return &genericrest.LocationStreamer{ Location: location, Transport: transport, ContentType: "text/plain", Flush: buildLogOpts.Follow, ResponseChecker: genericrest.NewGenericHttpResponseChecker("Pod", buildPodName), }, nil }
// Get retrieves a runtime.Object that will stream the contents of the pod log func (r *LogREST) Get(ctx api.Context, name string, opts runtime.Object) (runtime.Object, error) { logOpts, ok := opts.(*api.PodLogOptions) if !ok { return nil, fmt.Errorf("invalid options object: %#v", opts) } if errs := validation.ValidatePodLogOptions(logOpts); len(errs) > 0 { return nil, errors.NewInvalid(api.Kind("PodLogOptions"), name, errs) } location, transport, err := pod.LogLocation(r.Store, r.KubeletConn, ctx, name, logOpts) if err != nil { return nil, err } return &genericrest.LocationStreamer{ Location: location, Transport: transport, ContentType: "text/plain", Flush: logOpts.Follow, ResponseChecker: genericrest.NewGenericHttpResponseChecker(api.Resource("pods/log"), name), }, nil }
// Get returns a streamer resource with the contents of the deployment log func (r *REST) Get(ctx kapi.Context, name string, opts runtime.Object) (runtime.Object, error) { // Ensure we have a namespace in the context namespace, ok := kapi.NamespaceFrom(ctx) if !ok { return nil, errors.NewBadRequest("namespace parameter required.") } // Validate DeploymentLogOptions deployLogOpts, ok := opts.(*deployapi.DeploymentLogOptions) if !ok { return nil, errors.NewBadRequest("did not get an expected options.") } if errs := validation.ValidateDeploymentLogOptions(deployLogOpts); len(errs) > 0 { return nil, errors.NewInvalid(deployapi.Kind("DeploymentLogOptions"), "", errs) } // Fetch deploymentConfig and check latest version; if 0, there are no deployments // for this config config, err := r.dn.DeploymentConfigs(namespace).Get(name) if err != nil { return nil, errors.NewNotFound(deployapi.Resource("deploymentconfig"), name) } desiredVersion := config.Status.LatestVersion if desiredVersion == 0 { return nil, errors.NewBadRequest(fmt.Sprintf("no deployment exists for deploymentConfig %q", config.Name)) } // Support retrieving logs for older deployments switch { case deployLogOpts.Version == nil: // Latest or previous if deployLogOpts.Previous { desiredVersion-- if desiredVersion < 1 { return nil, errors.NewBadRequest(fmt.Sprintf("no previous deployment exists for deploymentConfig %q", config.Name)) } } case *deployLogOpts.Version <= 0 || *deployLogOpts.Version > config.Status.LatestVersion: // Invalid version return nil, errors.NewBadRequest(fmt.Sprintf("invalid version for deploymentConfig %q: %d", config.Name, *deployLogOpts.Version)) default: desiredVersion = *deployLogOpts.Version } // Get desired deployment targetName := deployutil.DeploymentNameForConfigVersion(config.Name, desiredVersion) target, err := r.waitForExistingDeployment(namespace, targetName) if err != nil { return nil, err } podName := deployutil.DeployerPodNameForDeployment(target.Name) // Check for deployment status; if it is new or pending, we will wait for it. If it is complete, // the deployment completed successfully and the deployer pod will be deleted so we will return a // success message. If it is running or failed, retrieve the log from the deployer pod. status := deployutil.DeploymentStatusFor(target) switch status { case deployapi.DeploymentStatusNew, deployapi.DeploymentStatusPending: if deployLogOpts.NoWait { glog.V(4).Infof("Deployment %s is in %s state. No logs to retrieve yet.", deployutil.LabelForDeployment(target), status) return &genericrest.LocationStreamer{}, nil } glog.V(4).Infof("Deployment %s is in %s state, waiting for it to start...", deployutil.LabelForDeployment(target), status) if err := deployutil.WaitForRunningDeployerPod(r.pn, target, r.timeout); err != nil { return nil, errors.NewBadRequest(fmt.Sprintf("failed to run deployer pod %s: %v", podName, err)) } latest, ok, err := registry.WaitForRunningDeployment(r.rn, target, r.timeout) if err != nil { return nil, errors.NewBadRequest(fmt.Sprintf("unable to wait for deployment %s to run: %v", deployutil.LabelForDeployment(target), err)) } if !ok { return nil, errors.NewServerTimeout(kapi.Resource("ReplicationController"), "get", 2) } if deployutil.DeploymentStatusFor(latest) == deployapi.DeploymentStatusComplete { podName, err = r.returnApplicationPodName(target) if err != nil { return nil, err } } case deployapi.DeploymentStatusComplete: podName, err = r.returnApplicationPodName(target) if err != nil { return nil, err } } logOpts := deployapi.DeploymentToPodLogOptions(deployLogOpts) location, transport, err := pod.LogLocation(&podGetter{r.pn}, r.connInfo, ctx, podName, logOpts) if err != nil { return nil, errors.NewBadRequest(err.Error()) } return &genericrest.LocationStreamer{ Location: location, Transport: transport, ContentType: "text/plain", Flush: deployLogOpts.Follow, ResponseChecker: genericrest.NewGenericHttpResponseChecker(kapi.Resource("pod"), podName), }, nil }
func TestRESTGet(t *testing.T) { ctx := kapi.NewDefaultContext() tests := []struct { testName string rest *REST name string opts runtime.Object expected runtime.Object expectedErr error }{ { testName: "running deployment", rest: mockREST(1, 1, api.DeploymentStatusRunning), name: "config", opts: &api.DeploymentLogOptions{Follow: true, Version: intp(1)}, expected: &genericrest.LocationStreamer{ Location: &url.URL{ Scheme: "https", Host: "some-host:12345", Path: "/containerLogs/default/config-1-deploy/config-1-deploy-container", RawQuery: "follow=true", }, Transport: nil, ContentType: "text/plain", Flush: true, ResponseChecker: genericrest.NewGenericHttpResponseChecker(kapi.Resource("pod"), "config-1-deploy"), }, expectedErr: nil, }, { testName: "complete deployment", rest: mockREST(5, 5, api.DeploymentStatusComplete), name: "config", opts: &api.DeploymentLogOptions{Follow: true, Version: intp(5)}, expected: &genericrest.LocationStreamer{ Location: &url.URL{ Scheme: "https", Host: "some-host:12345", Path: "/containerLogs/default/config-5-application-pod-1/config-5-container-1", RawQuery: "follow=true", }, Transport: nil, ContentType: "text/plain", Flush: true, ResponseChecker: genericrest.NewGenericHttpResponseChecker(kapi.Resource("pod"), "config-5-application-pod-1"), }, expectedErr: nil, }, { testName: "previous failed deployment", rest: mockREST(3, 2, api.DeploymentStatusFailed), name: "config", opts: &api.DeploymentLogOptions{Follow: false, Version: intp(2)}, expected: &genericrest.LocationStreamer{ Location: &url.URL{ Scheme: "https", Host: "some-host:12345", Path: "/containerLogs/default/config-2-deploy/config-2-deploy-container", }, Transport: nil, ContentType: "text/plain", Flush: false, ResponseChecker: genericrest.NewGenericHttpResponseChecker(kapi.Resource("pod"), "config-2-deploy"), }, expectedErr: nil, }, { testName: "previous deployment", rest: mockREST(3, 2, api.DeploymentStatusFailed), name: "config", opts: &api.DeploymentLogOptions{Follow: false, Previous: true}, expected: &genericrest.LocationStreamer{ Location: &url.URL{ Scheme: "https", Host: "some-host:12345", Path: "/containerLogs/default/config-2-deploy/config-2-deploy-container", }, Transport: nil, ContentType: "text/plain", Flush: false, ResponseChecker: genericrest.NewGenericHttpResponseChecker(kapi.Resource("pod"), "config-2-deploy"), }, expectedErr: nil, }, { testName: "non-existent previous deployment", rest: mockREST(1 /* won't be used */, 101, ""), name: "config", opts: &api.DeploymentLogOptions{Follow: false, Previous: true}, expected: nil, expectedErr: errors.NewBadRequest("no previous deployment exists for deploymentConfig \"config\""), }, } for _, test := range tests { got, err := test.rest.Get(ctx, test.name, test.opts) if err != nil && test.expectedErr != nil && err.Error() != test.expectedErr.Error() { t.Errorf("%s: error mismatch: expected %v, got %v", test.testName, test.expectedErr, err) continue } if err != nil && test.expectedErr == nil { t.Errorf("%s: error mismatch: expected no error, got %v", test.testName, err) continue } if err == nil && test.expectedErr != nil { t.Errorf("%s: error mismatch: expected %v, got no error", test.testName, test.expectedErr) continue } if !reflect.DeepEqual(got, test.expected) { t.Errorf("%s: location streamer mismatch: expected\n%#v\ngot\n%#v\n", test.testName, test.expected, got) e := test.expected.(*genericrest.LocationStreamer) a := got.(*genericrest.LocationStreamer) if e.Location.String() != a.Location.String() { t.Errorf("%s: expected url:\n%v\ngot:\n%v\n", test.testName, e.Location, a.Location) } } } }
// Get returns a streamer resource with the contents of the deployment log func (r *REST) Get(ctx kapi.Context, name string, opts runtime.Object) (runtime.Object, error) { // Ensure we have a namespace in the context namespace, ok := kapi.NamespaceFrom(ctx) if !ok { return nil, errors.NewBadRequest("namespace parameter required.") } // Validate DeploymentLogOptions deployLogOpts, ok := opts.(*deployapi.DeploymentLogOptions) if !ok { return nil, errors.NewBadRequest("did not get an expected options.") } if errs := validation.ValidateDeploymentLogOptions(deployLogOpts); len(errs) > 0 { return nil, errors.NewInvalid("deploymentLogOptions", "", errs) } // Fetch deploymentConfig and check latest version; if 0, there are no deployments // for this config config, err := r.ConfigGetter.DeploymentConfigs(namespace).Get(name) if err != nil { return nil, errors.NewNotFound("deploymentConfig", name) } desiredVersion := config.Status.LatestVersion if desiredVersion == 0 { return nil, errors.NewBadRequest(fmt.Sprintf("no deployment exists for deploymentConfig %q", config.Name)) } // Support retrieving logs for older deployments switch { case deployLogOpts.Version == nil: // Latest case *deployLogOpts.Version <= 0 || int(*deployLogOpts.Version) > config.Status.LatestVersion: // Invalid version return nil, errors.NewBadRequest(fmt.Sprintf("invalid version for deploymentConfig %q: %d", config.Name, *deployLogOpts.Version)) default: desiredVersion = int(*deployLogOpts.Version) } // Get desired deployment targetName := deployutil.DeploymentNameForConfigVersion(config.Name, desiredVersion) target, err := r.DeploymentGetter.ReplicationControllers(namespace).Get(targetName) if err != nil { return nil, err } // Check for deployment status; if it is new or pending, we will wait for it. If it is complete, // the deployment completed successfully and the deployer pod will be deleted so we will return a // success message. If it is running or failed, retrieve the log from the deployer pod. status := deployutil.DeploymentStatusFor(target) switch status { case deployapi.DeploymentStatusNew, deployapi.DeploymentStatusPending: if deployLogOpts.NoWait { glog.V(4).Infof("Deployment %s is in %s state. No logs to retrieve yet.", deployutil.LabelForDeployment(target), status) return &genericrest.LocationStreamer{}, nil } glog.V(4).Infof("Deployment %s is in %s state, waiting for it to start...", deployutil.LabelForDeployment(target), status) latest, ok, err := registry.WaitForRunningDeployment(r.DeploymentGetter, target, r.Timeout) if err != nil { return nil, errors.NewBadRequest(fmt.Sprintf("unable to wait for deployment %s to run: %v", deployutil.LabelForDeployment(target), err)) } if !ok { return nil, errors.NewTimeoutError(fmt.Sprintf("timed out waiting for deployment %s to start after %s", deployutil.LabelForDeployment(target), r.Timeout), 1) } if deployutil.DeploymentStatusFor(latest) == deployapi.DeploymentStatusComplete { // Deployer pod has been deleted, no logs to retrieve glog.V(4).Infof("Deployment %s was successful so the deployer pod is deleted. No logs to retrieve.", deployutil.LabelForDeployment(target)) return &genericrest.LocationStreamer{}, nil } case deployapi.DeploymentStatusComplete: // Deployer pod has been deleted, no logs to retrieve glog.V(4).Infof("Deployment %s was successful so the deployer pod is deleted. No logs to retrieve.", deployutil.LabelForDeployment(target)) return &genericrest.LocationStreamer{}, nil } // Setup url of the deployer pod deployPodName := deployutil.DeployerPodNameForDeployment(target.Name) logOpts := deployapi.DeploymentToPodLogOptions(deployLogOpts) location, transport, err := pod.LogLocation(r.PodGetter, r.ConnectionInfo, ctx, deployPodName, logOpts) if err != nil { return nil, errors.NewBadRequest(err.Error()) } return &genericrest.LocationStreamer{ Location: location, Transport: transport, ContentType: "text/plain", Flush: deployLogOpts.Follow, ResponseChecker: genericrest.NewGenericHttpResponseChecker("Pod", deployPodName), }, nil }
func TestRESTGet(t *testing.T) { ctx := kapi.NewDefaultContext() tests := []struct { testName string rest *REST name string opts runtime.Object expected runtime.Object expectedErr error }{ { testName: "running deployment", rest: mockREST(1, 1, api.DeploymentStatusRunning), name: "config", opts: &api.DeploymentLogOptions{Follow: true, Version: intp(1)}, expected: &genericrest.LocationStreamer{ Location: &url.URL{ Scheme: "https", Host: "config-1-deploy-host:12345", Path: "/containerLogs/default/config-1-deploy/config-1-deploy-container", RawQuery: "follow=true", }, Transport: nil, ContentType: "text/plain", Flush: true, ResponseChecker: genericrest.NewGenericHttpResponseChecker("Pod", "config-1-deploy"), }, expectedErr: nil, }, { testName: "complete deployment", rest: mockREST(5, 5, api.DeploymentStatusComplete), name: "config", opts: &api.DeploymentLogOptions{Follow: true, Version: intp(5)}, expected: &genericrest.LocationStreamer{}, expectedErr: nil, }, { testName: "previous failed deployment", rest: mockREST(3, 2, api.DeploymentStatusFailed), name: "config", opts: &api.DeploymentLogOptions{Follow: false, Version: intp(2)}, expected: &genericrest.LocationStreamer{ Location: &url.URL{ Scheme: "https", Host: "config-2-deploy-host:12345", Path: "/containerLogs/default/config-2-deploy/config-2-deploy-container", }, Transport: nil, ContentType: "text/plain", Flush: false, ResponseChecker: genericrest.NewGenericHttpResponseChecker("Pod", "config-2-deploy"), }, expectedErr: nil, }, } for _, test := range tests { got, err := test.rest.Get(ctx, test.name, test.opts) if err != test.expectedErr { t.Errorf("%s: error mismatch: expected %v, got %v", test.testName, test.expectedErr, err) continue } if !reflect.DeepEqual(got, test.expected) { t.Errorf("%s: location streamer mismatch: expected\n%#v\ngot\n%#v\n", test.testName, test.expected, got) if testing.Verbose() { e := test.expected.(*genericrest.LocationStreamer) a := got.(*genericrest.LocationStreamer) t.Errorf("%s: expected url:\n%v\ngot:\n%v\n", test.testName, e.Location, a.Location) } } } }