summaryrefslogtreecommitdiffstats
path: root/src/PublishPort.jsx
blob: 92a36cdccef7d682e99949f7b37f59292cbed294 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import React from 'react';

import { Button } from "@patternfly/react-core/dist/esm/components/Button";
import { FormGroup } from "@patternfly/react-core/dist/esm/components/Form";
import { FormSelect, FormSelectOption } from "@patternfly/react-core/dist/esm/components/FormSelect";
import { Grid } from "@patternfly/react-core/dist/esm/layouts/Grid";
import { TextInput } from "@patternfly/react-core/dist/esm/components/TextInput";
import { Popover } from "@patternfly/react-core/dist/esm/components/Popover";
import { OutlinedQuestionCircleIcon, TrashIcon } from '@patternfly/react-icons';
import cockpit from 'cockpit';
import ipaddr from "ipaddr.js";

import { FormHelper } from "cockpit-components-form-helper.jsx";
import * as utils from './util.js';

const _ = cockpit.gettext;

const MAX_PORT = 65535;

export function validatePublishPort(value, key) {
    switch (key) {
    case "IP":
        if (value && !ipaddr.isValid(value))
            return _("Must be a valid IP address");
        break;
    case "hostPort": {
        if (value) {
            const hostPort = parseInt(value);
            if (hostPort < 1 || hostPort > MAX_PORT)
                return _("1 to 65535");
        }

        break;
    }
    case "containerPort": {
        if (!value)
            return _("Container port must not be empty");

        const containerPort = parseInt(value);
        if (containerPort < 1 || containerPort > MAX_PORT)
            return _("1 to 65535");

        break;
    }
    default:
        console.error(`Unknown key "${key}"`); // not-covered: unreachable assertion
    }
}

export const PublishPort = ({ id, item, onChange, idx, removeitem, itemCount, validationFailed, onValidationChange }) =>
    (
        <Grid hasGutter id={id}>
            <FormGroup className="pf-m-6-col-on-md"
                id={id + "-ip-address-group"}
                label={_("IP address")}
                fieldId={id + "-ip-address"}
                labelIcon={
                    <Popover aria-label={_("IP address help")}
                        enableFlip
                        bodyContent={_("If host IP is set to 0.0.0.0 or not set at all, the port will be bound on all IPs on the host.")}>
                        <button onClick={e => e.preventDefault()} className="pf-v5-c-form__group-label-help">
                            <OutlinedQuestionCircleIcon />
                        </button>
                    </Popover>
                }>
                <TextInput id={id + "-ip-address"}
                        value={item.IP || ''}
                        validated={validationFailed?.IP ? "error" : "default"}
                        onChange={(_event, value) => {
                            utils.validationClear(validationFailed, "IP", onValidationChange);
                            utils.validationDebounce(() => onValidationChange({ ...validationFailed, IP: validatePublishPort(value, "IP") }));
                            onChange(idx, 'IP', value);
                        }} />
                <FormHelper helperTextInvalid={validationFailed?.IP} />
            </FormGroup>
            <FormGroup className="pf-m-2-col-on-md"
                    id={id + "-host-port-group"}
                    label={_("Host port")}
                    fieldId={id + "-host-port"}
                    labelIcon={
                        <Popover aria-label={_("Host port help")}
                            enableFlip
                            bodyContent={_("If the host port is not set the container port will be randomly assigned a port on the host.")}>
                            <button onClick={e => e.preventDefault()} className="pf-v5-c-form__group-label-help">
                                <OutlinedQuestionCircleIcon />
                            </button>
                        </Popover>
                    }>
                <TextInput id={id + "-host-port"}
                            type='number'
                            step={1}
                            min={1}
                            max={MAX_PORT}
                            value={item.hostPort || ''}
                            validated={validationFailed?.hostPort ? "error" : "default"}
                            onChange={(_event, value) => {
                                utils.validationClear(validationFailed, "hostPort", onValidationChange);
                                utils.validationDebounce(() => onValidationChange({ ...validationFailed, hostPort: validatePublishPort(value, "hostPort") }));
                                onChange(idx, 'hostPort', value);
                            }} />
                <FormHelper helperTextInvalid={validationFailed?.hostPort} />
            </FormGroup>
            <FormGroup className="pf-m-2-col-on-md"
                        id={id + "-container-port-group"}
                        label={_("Container port")}
                        fieldId={id + "-container-port"} isRequired>
                <TextInput id={id + "-container-port"}
                            type='number'
                            step={1}
                            min={1}
                            max={MAX_PORT}
                            validated={validationFailed?.containerPort ? "error" : "default"}
                            value={item.containerPort || ''}
                            onChange={(_event, value) => {
                                utils.validationClear(validationFailed, "containerPort", onValidationChange);
                                utils.validationDebounce(() => onValidationChange({ ...validationFailed, containerPort: validatePublishPort(value, "containerPort") }));
                                onChange(idx, 'containerPort', value);
                            }} />
                <FormHelper helperTextInvalid={validationFailed?.containerPort} />
            </FormGroup>
            <FormGroup className="pf-m-2-col-on-md"
                        label={_("Protocol")}
                        fieldId={id + "-protocol"}>
                <FormSelect className='pf-v5-c-form-control container-port-protocol'
                            id={id + "-protocol"}
                            value={item.protocol}
                            onChange={(_event, value) => onChange(idx, 'protocol', value)}>
                    <FormSelectOption value='tcp' key='tcp' label={_("TCP")} />
                    <FormSelectOption value='udp' key='udp' label={_("UDP")} />
                </FormSelect>
            </FormGroup>
            <FormGroup className="pf-m-1-col-on-md remove-button-group">
                <Button variant='plain'
                            className="btn-close"
                            id={id + "-btn-close"}
                            size="sm"
                            aria-label={_("Remove item")}
                            icon={<TrashIcon />}
                            onClick={() => removeitem(idx)} />
            </FormGroup>
        </Grid>
    );