Line data Source code
1 43 : import React, { useContext } from "react";
2 :
3 : import { Tooltip } from "@patternfly/react-core/dist/esm/components/Tooltip";
4 : import { debounce } from 'throttle-debounce';
5 :
6 : import cockpit from 'cockpit';
7 : import * as timeformat from 'timeformat';
8 :
9 43 : const _ = cockpit.gettext;
10 :
11 43 : export const PodmanInfoContext = React.createContext();
12 5 : export const usePodmanInfo = () => useContext(PodmanInfoContext);
13 :
14 43 : export const WithPodmanInfo = ({ value, children }) => {
15 43 : return (
16 43 : <PodmanInfoContext.Provider value={value}>
17 43 : {children}
18 43 : </PodmanInfoContext.Provider>
19 : );
20 43 : };
21 :
22 : // https://github.com/containers/podman/blob/main/libpod/define/containerstate.go
23 : // "Restarting" comes from special handling of restart case in Application.updateContainer()
24 43 : export const states = [_("Exited"), _("Paused"), _("Stopped"), _("Removing"), _("Configured"), _("Created"), _("Restart"), _("Running")];
25 :
26 : // https://github.com/containers/podman/blob/main/libpod/define/podstate.go
27 43 : export const podStates = [_("Created"), _("Running"), _("Stopped"), _("Paused"), _("Exited"), _("Error")];
28 :
29 43 : export const fallbackRegistries = ["docker.io", "quay.io"];
30 :
31 43 : export function debug(...args) {
32 0 : if (window.debugging === "all" || window.debugging?.includes("podman"))
33 0 : console.debug("podman", ...args);
34 43 : }
35 :
36 : // containers, pods, images states are indexed by these keys, to make the container IDs
37 : // globally unique across users
38 40 : export const makeKey = (uid, id) => `${uid ?? "user"}-${id}`;
39 :
40 43 : export function truncate_id(id) {
41 0 : if (!id) {
42 0 : return "";
43 0 : }
44 43 : return id.substring(0, 12);
45 43 : }
46 :
47 : // this supports formatted strings (via Date.parse) or raw timestamps
48 43 : export const RelativeTime = ({ time }) => {
49 43 : if (!time)
50 0 : return null;
51 41 : const timestamp = typeof time === "string" ? Date.parse(time) : time;
52 43 : const dateRel = timeformat.distanceToNow(timestamp);
53 43 : const dateAbs = timeformat.dateTimeSeconds(timestamp);
54 43 : return <Tooltip content={dateAbs}><span>{dateRel}</span></Tooltip>;
55 43 : };
56 :
57 : /*
58 : * The functions quote_cmdline and unquote_cmdline implement
59 : * a simple shell-like quoting syntax. They are used when letting the
60 : * user edit a sequence of words as a single string.
61 : *
62 : * When parsing, words are separated by whitespace. Single and double
63 : * quotes can be used to protect a sequence of characters that
64 : * contains whitespace or the other quote character. A backslash can
65 : * be used to protect any character. Quotes can appear in the middle
66 : * of a word.
67 : */
68 :
69 43 : export function quote_cmdline(words) {
70 15 : words = words || [];
71 :
72 43 : function is_whitespace(c) {
73 43 : return c == ' ';
74 43 : }
75 :
76 43 : function quote(word) {
77 43 : let text = "";
78 43 : let quote_char = "";
79 43 : let i;
80 43 : for (i = 0; i < word.length; i++) {
81 43 : if (word[i] == '\\' || word[i] == quote_char)
82 2 : text += '\\';
83 43 : else if (quote_char === "") {
84 43 : if (word[i] == "'" || is_whitespace(word[i]))
85 10 : quote_char = '"';
86 43 : else if (word[i] == '"')
87 0 : quote_char = "'";
88 43 : }
89 43 : text += word[i];
90 43 : }
91 :
92 43 : return quote_char + text + quote_char;
93 43 : }
94 :
95 43 : return words.map(quote).join(' ');
96 43 : }
97 :
98 15 : export function unquote_cmdline(text) {
99 15 : const words = [];
100 15 : let next;
101 :
102 15 : function is_whitespace(c) {
103 15 : return c == ' ';
104 15 : }
105 :
106 15 : function skip_whitespace() {
107 15 : while (next < text.length && is_whitespace(text[next]))
108 15 : next++;
109 15 : }
110 :
111 15 : function parse_word() {
112 15 : let word = "";
113 15 : let quote_char = null;
114 :
115 15 : while (next < text.length) {
116 0 : if (text[next] == '\\') {
117 0 : next++;
118 0 : if (next < text.length) {
119 0 : word += text[next];
120 0 : }
121 0 : } else if (text[next] == quote_char) {
122 6 : quote_char = null;
123 6 : } else if (quote_char) {
124 6 : word += text[next];
125 6 : } else if (text[next] == '"' || text[next] == "'") {
126 6 : quote_char = text[next];
127 6 : } else if (is_whitespace(text[next])) {
128 6 : break;
129 6 : } else
130 15 : word += text[next];
131 15 : next++;
132 15 : }
133 15 : return word;
134 15 : }
135 :
136 15 : next = 0;
137 15 : skip_whitespace();
138 15 : while (next < text.length) {
139 15 : words.push(parse_word());
140 15 : skip_whitespace();
141 15 : }
142 :
143 15 : return words;
144 15 : }
145 :
146 43 : export function image_name(image) {
147 4 : return image.RepoTags ? image.RepoTags[0] : "<none>:<none>";
148 43 : }
149 :
150 4 : export function is_valid_container_name(name) {
151 4 : return /^[a-zA-Z0-9][a-zA-Z0-9_\\.-]*$/.test(name);
152 4 : }
153 :
154 : /* Clears a single field in validationFailed object.
155 : *
156 : * Arguments:
157 : * - validationFailed (object): Object containing list of fields with validation error
158 : * - key (string): Specified which field from validationFailed object is clear
159 : * - onValidationChange (func)
160 : */
161 16 : export const validationClear = (validationFailed, key, onValidationChange) => {
162 16 : if (!validationFailed)
163 16 : return;
164 :
165 16 : const delta = { ...validationFailed };
166 16 : delete delta[key];
167 16 : onValidationChange(delta);
168 16 : };
169 :
170 : // This method needs to be outside of component as re-render would create a new instance of debounce
171 16 : export const validationDebounce = debounce(500, (validationHandler) => validationHandler());
|