Skip to content
This repository has been archived by the owner on Jan 3, 2024. It is now read-only.

[WIP] object endpoint #1

Open
wants to merge 45 commits into
base: object-endpoint
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
54b4631
Add Object Storage Tab
m-ildefons Jul 27, 2023
234d67f
feat(volume) Add volume scheduling failure message
smallteeths Aug 22, 2023
607c149
fix(volume) Add tooltip for volumes in maintenance mode
smallteeths Jul 24, 2023
bece277
fix: permission denied when running ui image
mantissahz Aug 23, 2023
ccc88e0
Make some of the delete confirmation dialogs nicer
yardenshoham Sep 11, 2023
001869b
Apply suggestions from code review
yardenshoham Oct 24, 2023
bf7821a
Replace spec.engineImage field in volume, engine and replica CRDs wit…
votdev Oct 20, 2023
63b0d38
Duplicate MIME type "text/html" in /var/config/nginx/nginx.conf
votdev Oct 30, 2023
c07d6bb
Add Object Storage Tab
m-ildefons Jul 27, 2023
ead6c7f
Object Endpoints: fill out list
m-ildefons Jul 27, 2023
f22f867
Object Endpoint: Fill data into the list
m-ildefons Jul 27, 2023
c17f426
Object Endpoint: Make websockets work
m-ildefons Jul 28, 2023
78fd559
Oject Endpoint: Separate Size Input from Volume
m-ildefons Jul 28, 2023
b23cf53
Object Endpoint: Delete action
m-ildefons Jul 28, 2023
5fa06b4
Object Endpoint: Default Values
m-ildefons Jul 28, 2023
ccd67a9
Object Endpoint: Add color/style to the state
m-ildefons Aug 1, 2023
c2b591a
object endpoint: make storageclass selectable
m-ildefons Aug 4, 2023
eddc893
Object Endpoint: Create Stub Edit dialogue
m-ildefons Aug 16, 2023
cc43e42
object store: rename, adjust creation dialogue
m-ildefons Sep 6, 2023
b128ee7
Various improvements
votdev Sep 6, 2023
bd555d8
Code cleanup & improvements
votdev Sep 6, 2023
34223a9
object store: more options
m-ildefons Sep 12, 2023
d0e5ae0
object store: fix websocket, add state colors
m-ildefons Sep 12, 2023
961924d
Populate edit dialog
votdev Sep 12, 2023
825ed83
Various enhancements
votdev Sep 13, 2023
9c7ce30
Fix table sizing/resizing
votdev Sep 13, 2023
543276d
Fix coloring of ObjectStore state and capitalize the text
votdev Sep 13, 2023
25b9163
Disable `Delete` bulk action button if not all selected rows can be d…
votdev Sep 13, 2023
504803c
Fix operation column to the right side
votdev Sep 13, 2023
fdbbadb
Add divider to menu
votdev Sep 13, 2023
1e7b1c7
object store: serve longhorn UI from /longhorn
m-ildefons Oct 20, 2023
c5e1281
object store: fix model functions
m-ildefons Oct 26, 2023
0f9ee0c
object store: endpoint creation
m-ildefons Oct 31, 2023
c03f4b7
object store: serve longhorn UI from /longhorn
m-ildefons Oct 20, 2023
c766877
object store: make endpoint input work
m-ildefons Nov 2, 2023
c676c4e
Various UI improvements
votdev Nov 2, 2023
2b94c3e
object store: proxy object store UI
m-ildefons Nov 6, 2023
de3a8a7
object store: disable request body size limit
m-ildefons Nov 7, 2023
a6a514f
object store: fix websockets
m-ildefons Nov 8, 2023
e3363aa
object store: stop/start object stores
m-ildefons Nov 8, 2023
7d78aef
object store: display space usage, filters
m-ildefons Nov 9, 2023
29f1b7a
object store: fix flickering keys
m-ildefons Nov 9, 2023
82d47cf
object store: fix volume expansion, from backup
m-ildefons Nov 14, 2023
8abb391
object store: remove unused code path
m-ildefons Nov 16, 2023
4ce6b89
object store: fix TLS secret bug
m-ildefons Nov 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ bin/
yarn.lock
npm-debug.log
*.DS_Store

# IDEs and editors
/.idea
16 changes: 12 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,28 @@ RUN npm run build
FROM registry.suse.com/bci/bci-base:15.4

RUN zypper -n ref && \
zypper -n install curl libxml2 bash gettext shadow nginx && \
rm -f /bin/sh && ln -s /bin/bash /bin/sh
zypper -n install curl libxml2 bash gettext shadow nginx awk

SHELL [ "/bin/bash", "-o", "pipefail", "-c" ]

RUN mkdir -p web/dist
WORKDIR /web

COPY --from=builder /web/dist /web/dist
COPY --from=builder /web/nginx.conf.template /etc/nginx/nginx.conf.template
COPY nginx.conf.template /etc/nginx/nginx.conf.template
COPY entrypoint.sh /entrypoint.sh

EXPOSE 8000
ENV LONGHORN_MANAGER_IP http://localhost:9500
ENV LONGHORN_UI_PORT 8000
ENV LONGHORN_NAMESPACE longhorn-system

RUN mkdir -p /var/config/nginx/ \
&& cp -r /etc/nginx/* /var/config/nginx/ \
&& touch /var/run/nginx.pid \
&& chown -R 499 /var/config /var/run/nginx.pid

# Use the uid of the default user (nginx) from the installed nginx package
USER 499

CMD ["/bin/bash", "-c", "mkdir -p /var/config/nginx/ && cp -r /etc/nginx/* /var/config/nginx/; envsubst '${LONGHORN_MANAGER_IP},${LONGHORN_UI_PORT}' < /etc/nginx/nginx.conf.template > /var/config/nginx/nginx.conf && nginx -c /var/config/nginx/nginx.conf -g 'daemon off;'"]
ENTRYPOINT ["/entrypoint.sh"]
13 changes: 13 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash

NAMESERVER_IP="$(grep -E '^nameserver' /etc/resolv.conf | head -n 1 | awk '{print $2}')"
LONGHORN_NAMESPACE_DOMAIN="$(grep -E '^search' /etc/resolv.conf | head -n 1 | awk '{print $2}')"

export NAMESERVER_IP
export LONGHORN_NAMESPACE_DOMAIN

envsubst '${LONGHORN_MANAGER_IP},${LONGHORN_UI_PORT},${LONGHORN_NAMESPACE_DOMAIN},${NAMESERVER_IP}' \
< /etc/nginx/nginx.conf.template \
> /var/config/nginx/nginx.conf

nginx -c /var/config/nginx/nginx.conf
23 changes: 17 additions & 6 deletions nginx.conf.template
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
events { worker_connections 1024; }
daemon off;

error_log /dev/stdout info;

http {
access_log /dev/stdout;

server {
gzip on;
gzip_min_length 1k;
gzip_comp_level 2;
gzip_types text/html text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png image/svg+xml;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png image/svg+xml;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
include /etc/nginx/mime.types;
Expand Down Expand Up @@ -34,16 +40,21 @@ http {
client_max_body_size 0;
}

location ~ ^/.(images|javascript|js|css|flash|media|static)/ {
root /web/dist;
}

location ~ ^/objectstore/([^/]+) {
resolver ${NAMESERVER_IP};
client_max_body_size 0;
proxy_pass http://$1.${LONGHORN_NAMESPACE_DOMAIN}:8080;
}

location / {
root /web/dist;
index index.html;
add_header Cache-Control "max-age=0";
try_files $uri $uri/ /index.html =404;
}

location ~ ^/.(images|javascript|js|css|flash|media|static)/ {
root /web/dist;
}

}
}
3 changes: 3 additions & 0 deletions src/components/DropOption/DropOption.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { Dropdown, Button, Icon, Menu, Tooltip } from 'antd'

const DropOption = ({ onMenuClick, menuOptions = [], buttonStyle, dropdownProps, tooltipProps }) => {
const menu = menuOptions.map(item => {
if (item.type === 'divider') {
return (<Menu.Divider key={Math.random()} />)
}
const tooltip = item.tooltip !== undefined ? item.tooltip : ''
return (
<Menu.Item key={item.key} disabled={!!item.disabled}>
Expand Down
53 changes: 53 additions & 0 deletions src/components/EndpointInput/EndpointInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Form, Select, Input } from 'antd'

const FormItem = Form.Item
const Option = Select.Option

class EndpointInput extends React.Component {
state = {
domainName: 'example.com',
tlsSecret: '',
}

render() {
const { getFieldDecorator, getFieldsValue, setFieldsValue } = this.props
const { tlsSecrets } = this.props

function secretSelect(value) {
setFieldsValue({
...getFieldsValue(),
tlsSecret: value,
})
}

return (
<div style={{ display: 'flex' }}>
<FormItem label="Domain Name" style={{ flex: 0.6 }} labelCol={{ span: 8 }} wrapperCol={{ span: 14 }}>
{getFieldDecorator('domainName', {})(<Input placeholder={this.state.domainName} />)}
</FormItem>

<FormItem label="TLS Secret" style={{ flex: 0.6 }} labelCol={{ span: 8 }} apperCol={{ span: 14 }}>
{getFieldDecorator('tlsSecret', {})(
<Select onChange={secretSelect}>
{tlsSecrets.map((opt) => {
return (<Option key={opt.name}>{opt.name}</Option>)
})}
</Select>
)}
</FormItem>
</div>
)
}
}

EndpointInput.propTypes = {
state: PropTypes.object,
tlsSecrets: PropTypes.array,
getFieldDecorator: PropTypes.func,
getFieldsValue: PropTypes.func,
setFieldsValue: PropTypes.func,
}

export default EndpointInput
8 changes: 6 additions & 2 deletions src/components/Layout/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import semver from 'semver'
import BundlesModel from './BundlesModel'
import StableLonghornVersions from './StableLonghornVersions'

function Footer({ app, host, volume, setting, engineimage, eventlog, backingImage, recurringJob, backup, systemBackups, dispatch }) {
function Footer({ app, host, volume, setting, engineimage, eventlog, backingImage, recurringJob, objectstorage, secret, backup, systemBackups, dispatch }) {
const { bundlesropsVisible, bundlesropsKey, stableLonghornVersionslVisible, stableLonghornVersionsKey, okText, modalButtonDisabled, progressPercentage } = app
const currentVersion = config.version === '${VERSION}' ? 'dev' : config.version // eslint-disable-line no-template-curly-in-string
const issueHref = 'https://github.com/longhorn/longhorn/issues/new/choose'
Expand Down Expand Up @@ -128,6 +128,8 @@ function Footer({ app, host, volume, setting, engineimage, eventlog, backingImag
{getStatusIcon(eventlog)}
{getStatusIcon(backingImage)}
{getStatusIcon(recurringJob)}
{getStatusIcon(objectstorage)}
{getStatusIcon(secret)}
{getBackupStatusIcon(backup, 'backupVolumes')}
{getBackupStatusIcon(backup, 'backups')}
{getSystemBackupStatusIcon(systemBackups, 'systemBackup')}
Expand All @@ -148,10 +150,12 @@ Footer.propTypes = {
eventlog: PropTypes.object,
backingImage: PropTypes.object,
recurringJob: PropTypes.object,
objectstorage: PropTypes.object,
secret: PropTypes.object,
app: PropTypes.object,
backup: PropTypes.object,
systemBackups: PropTypes.object,
dispatch: PropTypes.func,
}

export default connect(({ app, host, volume, setting, engineimage, eventlog, backingImage, recurringJob, backup, systemBackups }) => ({ app, host, volume, setting, engineimage, eventlog, backingImage, recurringJob, backup, systemBackups }))(Footer)
export default connect(({ app, host, volume, setting, engineimage, eventlog, backingImage, recurringJob, objectstorage, secret, backup, systemBackups }) => ({ app, host, volume, setting, engineimage, eventlog, backingImage, recurringJob, objectstorage, secret, backup, systemBackups }))(Footer)
2 changes: 1 addition & 1 deletion src/components/Replica/Replica.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Replica extends React.Component {
switch (event.key) {
case 'delete':
confirm({
title: `Are you sure you want to delete replica ${record.name} ?`,
title: `Are you sure you want to delete replica ${record.name}?`,
onOk() {
deleteReplicas([record])
},
Expand Down
99 changes: 99 additions & 0 deletions src/components/SizeInput/SizeInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Form, Select, InputNumber } from 'antd'

const FormItem = Form.Item
const Option = Select.Option

class SizeInput extends React.Component {
state = {
size: 1,
unit: 'Gi',
mustExpand: false,
}

render() {
const { getFieldDecorator, getFieldsValue, setFieldsValue } = this.props
const { size, unit, mustExpand } = this.props.state

function unitChange(value) {
const unitmap = new Map([
['Mi', 0],
['Gi', 1],
['Ti', 2],
])

let currentSize = getFieldsValue().size
let newUnit = unitmap.get(value)
let currentUnit = unitmap.get(getFieldsValue().unit)

if (newUnit > currentUnit) {
currentSize /= 1024 ** (newUnit - currentUnit)
} else {
currentSize *= 1024 ** (currentUnit - newUnit)
}
setFieldsValue({
...getFieldsValue(),
unit: value,
size: currentSize,
})
}

return (
<div style={{ display: 'flex' }}>
<FormItem label="Size" style={{ flex: 0.6 }} labelCol={{ span: 8 }} wrapperCol={{ span: 14 }}>
{getFieldDecorator('size', {
initialValue: size,
rules: [
{
required: true,
message: 'Please input size',
}, {
validator: (rule, value, callback) => {
if (value === '' || typeof value !== 'number') {
callback()
return
}
if (value < 0 || value > 65536) {
callback('The value should be between 0 and 65535')
} else if (!/^\d+([.]\d{1,2})?$/.test(value)) {
callback('This value should have at most two decimal places')
} else if (mustExpand && value < size) {
callback(`Size should be larger than ${size} ${unit}`)
} else if (value < 10 && getFieldsValue().unit === 'Mi') {
callback('The volume size must be greater than 10 Mi')
} else if (value % 1 !== 0 && getFieldsValue().unit === 'Mi') {
callback('Decimals are not allowed')
} else {
callback()
}
},
},
],
})(<InputNumber style={{ width: '250px' }} />)}
</FormItem>
<FormItem>
{getFieldDecorator('unit', {
initialValue: unit,
rules: [{ required: true, message: 'Please select your unit!' }],
})(
<Select style={{ width: '100px' }} onChange={unitChange} placeholder="GiB">
<Option value="Mi">MiB</Option>
<Option value="Gi">GiB</Option>
<Option value="Ti">TiB</Option>
</Select>,
)}
</FormItem>
</div>
)
}
}

SizeInput.propTypes = {
state: PropTypes.object,
getFieldDecorator: PropTypes.func,
getFieldsValue: PropTypes.func,
setFieldsValue: PropTypes.func,
}

export default SizeInput
4 changes: 4 additions & 0 deletions src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import BackupLabelInput from './BackupLabelInput/BackupLabelInput'
import BackupLabelInputForRecurring from './BackupLabelInputForRecurring/BackupLabelInputForRecurring'
import ExpansionErrorDetail from './ExpansionErrorDetail/ExpansionErrorDetail'
import AutoComplete from './AutoComplete/AutoComplete'
import SizeInput from './SizeInput/SizeInput'
import EndpointInput from './EndpointInput/EndpointInput'

export {
DropOption,
Expand All @@ -36,4 +38,6 @@ export {
BackupLabelInputForRecurring,
ExpansionErrorDetail,
AutoComplete,
SizeInput,
EndpointInput,
}
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import recurringJob from './models/recurringJob'
import instanceManager from './models/instanceManager'
import orphanedData from './models/orphanedData'
import systemBackups from './models/systemBackups'
import objectStore from './models/objectStore'
import secret from './models/secret'

// import assets
import './assets/iconfont/iconfont.eot'
Expand Down Expand Up @@ -40,6 +42,8 @@ app.model(recurringJob)
app.model(instanceManager)
app.model(orphanedData)
app.model(systemBackups)
app.model(objectStore)
app.model(secret)

// 3. Router
app.router(routerConfig)
Expand Down
Empty file modified src/models/dashboard.js
100755 → 100644
Empty file.
Empty file modified src/models/engineimage.js
100755 → 100644
Empty file.
Empty file modified src/models/eventlog.js
100755 → 100644
Empty file.
2 changes: 1 addition & 1 deletion src/models/host.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export default {
})
engineImageData.forEach((item) => {
replicaData.forEach((ele) => {
if (item.engineImage === ele.engineImage) {
if (item.image === ele.image) {
item.replicaCurrentState = ele.currentState
}
})
Expand Down
Loading