Skip to content

Commit

Permalink
Merge pull request #1290 from weaveworks/remove-container-control
Browse files Browse the repository at this point in the history
Add control for removing stopped docker containers.
  • Loading branch information
paulbellamy committed Apr 22, 2016
2 parents f727bd6 + eda2a20 commit 1539e66
Show file tree
Hide file tree
Showing 14 changed files with 82 additions and 19 deletions.
8 changes: 8 additions & 0 deletions client/app/scripts/actions/app-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,14 @@ export function receiveApiDetails(apiDetails) {
});
}

export function receiveControlNodeRemoved(nodeId) {
AppDispatcher.dispatch({
type: ActionTypes.RECEIVE_CONTROL_NODE_REMOVED,
nodeId
});
updateRoute();
}

export function receiveControlPipeFromParams(pipeId, rawTty) {
// TODO add nodeId
AppDispatcher.dispatch({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import _ from 'lodash';

import NodeDetailsControlButton from './node-details-control-button';

Expand All @@ -17,7 +18,7 @@ export default function NodeDetailsControls({controls, error, nodeId, pending})
<span className="node-details-controls-error-messages">{error}</span>
</div>}
<span className="node-details-controls-buttons">
{controls && controls.map(control => <NodeDetailsControlButton
{_.sortBy(controls, 'rank').map(control => <NodeDetailsControlButton
nodeId={nodeId} control={control} pending={pending} key={control.id} />)}
</span>
{controls && <span title="Applying..." className={spinnerClassName}></span>}
Expand Down
1 change: 1 addition & 0 deletions client/app/scripts/constants/action-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const ACTION_TYPES = [
'PIN_METRIC',
'UNPIN_METRIC',
'OPEN_WEBSOCKET',
'RECEIVE_CONTROL_NODE_REMOVED',
'RECEIVE_CONTROL_PIPE',
'RECEIVE_CONTROL_PIPE_STATUS',
'RECEIVE_NODE_DETAILS',
Expand Down
5 changes: 5 additions & 0 deletions client/app/scripts/stores/app-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,11 @@ export class AppStore extends Store {
this.__emitChange();
break;
}
case ActionTypes.RECEIVE_CONTROL_NODE_REMOVED: {
closeNodeDetails(payload.nodeId);
this.__emitChange();
break;
}
case ActionTypes.RECEIVE_CONTROL_PIPE: {
controlPipes = controlPipes.set(payload.pipeId, makeOrderedMap({
id: payload.pipeId,
Expand Down
13 changes: 9 additions & 4 deletions client/app/scripts/utils/web-api-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import reqwest from 'reqwest';

import { clearControlError, closeWebsocket, openWebsocket, receiveError,
receiveApiDetails, receiveNodesDelta, receiveNodeDetails, receiveControlError,
receiveControlPipe, receiveControlPipeStatus, receiveControlSuccess,
receiveTopologies, receiveNotFound } from '../actions/app-actions';
receiveControlNodeRemoved, receiveControlPipe, receiveControlPipeStatus,
receiveControlSuccess, receiveTopologies, receiveNotFound } from '../actions/app-actions';

import { API_INTERVAL, TOPOLOGY_INTERVAL } from '../constants/timer';

Expand Down Expand Up @@ -184,8 +184,13 @@ export function doControlRequest(nodeId, control) {
url,
success: (res) => {
receiveControlSuccess(nodeId);
if (res && res.pipe) {
receiveControlPipe(res.pipe, nodeId, res.raw_tty, true);
if (res) {
if (res.pipe) {
receiveControlPipe(res.pipe, nodeId, res.raw_tty, true);
}
if (res.removedNode) {
receiveControlNodeRemoved(nodeId);
}
}
},
error: (err) => {
Expand Down
13 changes: 9 additions & 4 deletions common/xfer/controls.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@ type Request struct {

// Response is the Probe -> App -> UI message type for the control RPCs.
type Response struct {
Value interface{} `json:"value,omitempty"`
Error string `json:"error,omitempty"`
Pipe string `json:"pipe,omitempty"`
RawTTY bool `json:"raw_tty,omitempty"`
Value interface{} `json:"value,omitempty"`
Error string `json:"error,omitempty"`

// Pipe specific fields
Pipe string `json:"pipe,omitempty"`
RawTTY bool `json:"raw_tty,omitempty"`

// Remove specific fields
RemovedNode string `json:"removedNode,omitempty"` // Set if node was removed
}

// Message is the unions of Request, Response and arbitrary Value.
Expand Down
2 changes: 1 addition & 1 deletion probe/docker/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ func (c *container) GetNode(localAddrs []net.IP) report.Node {
RestartContainer, StopContainer, PauseContainer, AttachContainer, ExecContainer,
)
} else {
result = result.WithControls(StartContainer)
result = result.WithControls(StartContainer, RemoveContainer)
}

result = result.AddTable(LabelPrefix, c.container.Config.Labels)
Expand Down
15 changes: 15 additions & 0 deletions probe/docker/controls.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const (
RestartContainer = "docker_restart_container"
PauseContainer = "docker_pause_container"
UnpauseContainer = "docker_unpause_container"
RemoveContainer = "docker_remove_container"
AttachContainer = "docker_attach_container"
ExecContainer = "docker_exec_container"

Expand Down Expand Up @@ -48,6 +49,18 @@ func (r *registry) unpauseContainer(containerID string, _ xfer.Request) xfer.Res
return xfer.ResponseError(r.client.UnpauseContainer(containerID))
}

func (r *registry) removeContainer(containerID string, _ xfer.Request) xfer.Response {
log.Infof("Removing container %s", containerID)
if err := r.client.RemoveContainer(docker_client.RemoveContainerOptions{
ID: containerID,
}); err != nil {
return xfer.ResponseError(err)
}
return xfer.Response{
RemovedNode: containerID,
}
}

func (r *registry) attachContainer(containerID string, req xfer.Request) xfer.Response {
c, ok := r.GetContainer(containerID)
if !ok {
Expand Down Expand Up @@ -156,6 +169,7 @@ func (r *registry) registerControls() {
controls.Register(RestartContainer, captureContainerID(r.restartContainer))
controls.Register(PauseContainer, captureContainerID(r.pauseContainer))
controls.Register(UnpauseContainer, captureContainerID(r.unpauseContainer))
controls.Register(RemoveContainer, captureContainerID(r.removeContainer))
controls.Register(AttachContainer, captureContainerID(r.attachContainer))
controls.Register(ExecContainer, captureContainerID(r.execContainer))
}
Expand All @@ -166,6 +180,7 @@ func (r *registry) deregisterControls() {
controls.Rm(RestartContainer)
controls.Rm(PauseContainer)
controls.Rm(UnpauseContainer)
controls.Rm(RemoveContainer)
controls.Rm(AttachContainer)
controls.Rm(ExecContainer)
}
1 change: 1 addition & 0 deletions probe/docker/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ type Client interface {
RestartContainer(string, uint) error
PauseContainer(string) error
UnpauseContainer(string) error
RemoveContainer(docker_client.RemoveContainerOptions) error
AttachToContainerNonBlocking(docker_client.AttachToContainerOptions) (docker_client.CloseWaiter, error)
CreateExec(docker_client.CreateExecOptions) (*docker_client.Exec, error)
StartExecNonBlocking(string, docker_client.StartExecOptions) (docker_client.CloseWaiter, error)
Expand Down
4 changes: 4 additions & 0 deletions probe/docker/registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ func (m *mockDockerClient) UnpauseContainer(_ string) error {
return fmt.Errorf("unpaused")
}

func (m *mockDockerClient) RemoveContainer(_ client.RemoveContainerOptions) error {
return fmt.Errorf("remove")
}

type mockCloseWaiter struct{}

func (mockCloseWaiter) Close() error { return nil }
Expand Down
31 changes: 22 additions & 9 deletions probe/docker/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,39 +101,52 @@ func (r *Reporter) containerTopology(localAddrs []net.IP) report.Topology {
WithMetricTemplates(ContainerMetricTemplates).
WithTableTemplates(ContainerTableTemplates)
result.Controls.AddControl(report.Control{
ID: StopContainer,
Human: "Stop",
Icon: "fa-stop",
ID: AttachContainer,
Human: "Attach",
Icon: "fa-desktop",
Rank: 1,
})
result.Controls.AddControl(report.Control{
ID: ExecContainer,
Human: "Exec shell",
Icon: "fa-terminal",
Rank: 2,
})
result.Controls.AddControl(report.Control{
ID: StartContainer,
Human: "Start",
Icon: "fa-play",
Rank: 3,
})
result.Controls.AddControl(report.Control{
ID: RestartContainer,
Human: "Restart",
Icon: "fa-repeat",
Rank: 4,
})
result.Controls.AddControl(report.Control{
ID: PauseContainer,
Human: "Pause",
Icon: "fa-pause",
Rank: 5,
})
result.Controls.AddControl(report.Control{
ID: UnpauseContainer,
Human: "Unpause",
Icon: "fa-play",
Rank: 6,
})
result.Controls.AddControl(report.Control{
ID: AttachContainer,
Human: "Attach",
Icon: "fa-desktop",
ID: StopContainer,
Human: "Stop",
Icon: "fa-stop",
Rank: 7,
})
result.Controls.AddControl(report.Control{
ID: ExecContainer,
Human: "Exec shell",
Icon: "fa-terminal",
ID: RemoveContainer,
Human: "Remove",
Icon: "fa-trash-o",
Rank: 8,
})

metadata := map[string]string{report.ControlProbeID: r.probeID}
Expand Down
1 change: 1 addition & 0 deletions probe/kubernetes/reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ func (r *Reporter) podTopology(services []Service) (report.Topology, report.Topo
ID: GetLogs,
Human: "Get logs",
Icon: "fa-desktop",
Rank: 0,
})
for _, service := range services {
selectors[service.ID()] = service.Selector()
Expand Down
3 changes: 3 additions & 0 deletions render/detailed/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type wiredControlInstance struct {
ID string `json:"id"`
Human string `json:"human"`
Icon string `json:"icon"`
Rank int `json:"rank"`
}

// CodecEncodeSelf marshals this ControlInstance. It takes the basic Metric
Expand All @@ -56,6 +57,7 @@ func (c *ControlInstance) CodecEncodeSelf(encoder *codec.Encoder) {
ID: c.Control.ID,
Human: c.Control.Human,
Icon: c.Control.Icon,
Rank: c.Control.Rank,
})
}

Expand All @@ -70,6 +72,7 @@ func (c *ControlInstance) CodecDecodeSelf(decoder *codec.Decoder) {
ID: in.ID,
Human: in.Human,
Icon: in.Icon,
Rank: in.Rank,
},
}
}
Expand Down
1 change: 1 addition & 0 deletions report/controls.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Control struct {
ID string `json:"id"`
Human string `json:"human"`
Icon string `json:"icon"` // from https://fortawesome.github.io/Font-Awesome/cheatsheet/ please
Rank int `json:"rank"`
}

// Merge merges other with cs, returning a fresh Controls.
Expand Down

0 comments on commit 1539e66

Please sign in to comment.