mirror of
https://github.com/cuigh/swirl
synced 2024-12-27 06:13:00 +00:00
Correct handling host and ingress networks when editing service
This commit is contained in:
parent
63cc555e21
commit
70837391d1
@ -5,6 +5,7 @@
|
||||
> As this version contains some incompatible modifications, it is recommended to redeploy instead of upgrading directly.
|
||||
|
||||
* feat: Refactor UI with vue3.
|
||||
* feat: Add support to agent. Swirl can connect to any container even in swarm mode now.
|
||||
* feat: Switch to official MongoDB driver.
|
||||
* feat: Allow set chart margins.
|
||||
* fix: Some args are incorrect when generating service command line.
|
||||
|
@ -56,26 +56,31 @@ func (b *serviceBiz) Find(name string, status bool) (service *Service, raw strin
|
||||
}
|
||||
if err == nil {
|
||||
service = newService(&s)
|
||||
//err = b.fillNetworks(service, &s)
|
||||
err = b.fillNetworks(service)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (b *serviceBiz) fillNetworks(service *Service, s *swarm.Service) error {
|
||||
if len(s.Endpoint.VirtualIPs) == 0 {
|
||||
func (b *serviceBiz) fillNetworks(service *Service) error {
|
||||
if len(service.Endpoint.VIPs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var ids = make([]string, len(s.Endpoint.VirtualIPs))
|
||||
for i, vip := range s.Endpoint.VirtualIPs {
|
||||
ids[i] = vip.NetworkID
|
||||
var ids = make([]string, len(service.Endpoint.VIPs))
|
||||
for i, vip := range service.Endpoint.VIPs {
|
||||
ids[i] = vip.ID
|
||||
}
|
||||
|
||||
names, err := b.d.NetworkNames(context.TODO(), ids...)
|
||||
if err == nil {
|
||||
for i, vip := range s.Endpoint.VirtualIPs {
|
||||
ids[i] = names[vip.NetworkID] + ":" + vip.NetworkID
|
||||
for i := range service.Endpoint.VIPs {
|
||||
vip := &service.Endpoint.VIPs[i]
|
||||
vip.Name = names[vip.ID]
|
||||
// ingress network cannot be explicitly attached.
|
||||
if vip.Name != "ingress" {
|
||||
service.Networks = append(service.Networks, vip.Name)
|
||||
}
|
||||
}
|
||||
service.Networks = ids
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -215,7 +220,7 @@ type Service struct {
|
||||
Env data.Options `json:"env,omitempty"`
|
||||
Labels data.Options `json:"labels,omitempty"`
|
||||
ContainerLabels data.Options `json:"containerLabels,omitempty"`
|
||||
Networks []string `json:"networks,omitempty"`
|
||||
Networks []string `json:"networks,omitempty"` // only for edit
|
||||
Mounts []Mount `json:"mounts,omitempty"`
|
||||
Update struct {
|
||||
State string `json:"state,omitempty"`
|
||||
@ -548,11 +553,6 @@ func newService(s *swarm.Service) *Service {
|
||||
service.Update.Message = s.UpdateStatus.Message
|
||||
}
|
||||
|
||||
// Networks
|
||||
for _, n := range s.Spec.TaskTemplate.Networks {
|
||||
service.Networks = append(service.Networks, n.Target)
|
||||
}
|
||||
|
||||
// Endpoint
|
||||
service.Endpoint.Mode = s.Endpoint.Spec.Mode
|
||||
for _, vip := range s.Endpoint.VirtualIPs {
|
||||
|
98
compose.yml
98
compose.yml
@ -1,16 +1,16 @@
|
||||
version: '3'
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
swirl:
|
||||
image: cuigh/swirl
|
||||
environment:
|
||||
DB_ADDRESS: mongodb://mongo:27017/swirl
|
||||
DB_ADDRESS: mongodb://<database_address>
|
||||
DOCKER_ENDPOINT: tcp://swirl_manager_agent:2375
|
||||
AGENTS: swirl_manager_agent,swirl_worker_agent
|
||||
ports:
|
||||
- "8001:8001"
|
||||
networks:
|
||||
- net
|
||||
ports:
|
||||
- "8001:8001"
|
||||
deploy:
|
||||
replicas: 2
|
||||
placement:
|
||||
@ -18,10 +18,10 @@ services:
|
||||
|
||||
manager_agent:
|
||||
image: cuigh/socat
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
networks:
|
||||
- net
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
deploy:
|
||||
mode: global
|
||||
placement:
|
||||
@ -29,53 +29,63 @@ services:
|
||||
|
||||
worker_agent:
|
||||
image: cuigh/socat
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
networks:
|
||||
- net
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
deploy:
|
||||
mode: global
|
||||
placement:
|
||||
constraints: [ node.role == worker ]
|
||||
|
||||
# prometheus:
|
||||
# image: prom/prometheus
|
||||
# volumes:
|
||||
# - prometheus:/prometheus
|
||||
# networks:
|
||||
# - net
|
||||
# deploy:
|
||||
# replicas: 1
|
||||
# placement:
|
||||
# constraints: [ node.labels.app.prometheus == true ]
|
||||
|
||||
# cadvisor:
|
||||
# image: gcr.io/cadvisor/cadvisor
|
||||
# volumes:
|
||||
# - /:/rootfs:ro
|
||||
# - /sys:/sys:ro
|
||||
# - /var/lib/docker:/var/lib/docker:ro
|
||||
# - /var/run:/var/run:ro
|
||||
# - /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
# networks:
|
||||
# - net
|
||||
# deploy:
|
||||
# mode: global
|
||||
|
||||
mongo:
|
||||
image: mongo
|
||||
volumes:
|
||||
- mongo:/data/db
|
||||
networks:
|
||||
- net
|
||||
deploy:
|
||||
replicas: 1
|
||||
# prometheus:
|
||||
# image: prom/prometheus
|
||||
# networks:
|
||||
# - net
|
||||
# volumes:
|
||||
# - prometheus:/prometheus
|
||||
# configs:
|
||||
# - source: prometheus.yml
|
||||
# target: /etc/prometheus/prometheus.yml
|
||||
# deploy:
|
||||
# replicas: 1
|
||||
# placement:
|
||||
# constraints: [ node.labels.app.mongo == true ]
|
||||
# constraints: [ node.labels.app.prometheus == true ]
|
||||
|
||||
volumes:
|
||||
prometheus:
|
||||
mongo:
|
||||
# cadvisor:
|
||||
# image: gcr.io/cadvisor/cadvisor
|
||||
# networks:
|
||||
# - net
|
||||
# volumes:
|
||||
# - /:/rootfs:ro
|
||||
# - /dev/disk/:/dev/disk:ro
|
||||
# - /sys:/sys:ro
|
||||
# - /var/run:/var/run:ro
|
||||
# - /var/lib/docker:/var/lib/docker:ro
|
||||
# privileged: true
|
||||
# deploy:
|
||||
# mode: global
|
||||
#
|
||||
# node:
|
||||
# image: quay.io/prometheus/node-exporter
|
||||
# command:
|
||||
# - '--path.rootfs=/host'
|
||||
# networks:
|
||||
# - host
|
||||
# pid: host
|
||||
# volumes:
|
||||
# - /:/host:ro,rslave
|
||||
# deploy:
|
||||
# mode: global
|
||||
|
||||
#volumes:
|
||||
# prometheus:
|
||||
|
||||
#configs:
|
||||
# prometheus.yml:
|
||||
# external: true
|
||||
|
||||
networks:
|
||||
# host:
|
||||
# external: true
|
||||
net:
|
@ -38,6 +38,40 @@ func (d *Dao) SessionUpdateExpiry(ctx context.Context, id string, expiry time.Ti
|
||||
})
|
||||
}
|
||||
|
||||
func (d *Dao) SessionUpdateDirty(ctx context.Context, userID string, roleID string) (err error) {
|
||||
contains := func(arr []string, str string) bool {
|
||||
for _, s := range arr {
|
||||
if s == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var (
|
||||
buf []byte
|
||||
now = time.Now()
|
||||
)
|
||||
return d.db.Update(func(tx *bolt.Tx) (err error) {
|
||||
b := tx.Bucket([]byte(Session))
|
||||
return b.ForEach(func(k, v []byte) error {
|
||||
session := &model.Session{}
|
||||
if err = decode(v, session); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if (userID != "" && session.UserID == userID) || (roleID != "" && contains(session.Roles, roleID)) {
|
||||
session.Dirty = true
|
||||
session.UpdatedAt = now
|
||||
if buf, err = encode(session); err == nil {
|
||||
err = b.Put(k, buf)
|
||||
}
|
||||
}
|
||||
return err
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// SessionPrune cleans up expired logs.
|
||||
func (d *Dao) SessionPrune() {
|
||||
err := d.db.Update(func(tx *bolt.Tx) (err error) {
|
||||
|
@ -37,6 +37,7 @@ type Interface interface {
|
||||
SessionCreate(ctx context.Context, session *model.Session) error
|
||||
SessionUpdate(ctx context.Context, session *model.Session) error
|
||||
SessionUpdateExpiry(ctx context.Context, id string, expiry time.Time) (err error)
|
||||
SessionUpdateDirty(ctx context.Context, userID string, roleID string) (err error)
|
||||
|
||||
RegistryGet(ctx context.Context, id string) (*model.Registry, error)
|
||||
RegistryGetByURL(ctx context.Context, url string) (registry *model.Registry, err error)
|
||||
|
@ -36,3 +36,21 @@ func (d *Dao) SessionUpdateExpiry(ctx context.Context, id string, expiry time.Ti
|
||||
}
|
||||
return d.update(ctx, Session, id, update)
|
||||
}
|
||||
|
||||
func (d *Dao) SessionUpdateDirty(ctx context.Context, userID string, roleID string) (err error) {
|
||||
filter := bson.M{}
|
||||
if userID != "" {
|
||||
filter["userId"] = userID
|
||||
} else if roleID != "" {
|
||||
filter["roles"] = roleID
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
update := bson.M{
|
||||
"dirty": true,
|
||||
"updated_by": time.Now(),
|
||||
}
|
||||
_, err = d.db.Collection(Session).UpdateMany(ctx, filter, update)
|
||||
return
|
||||
}
|
||||
|
@ -4,8 +4,10 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cuigh/auxo/app/container"
|
||||
"github.com/cuigh/auxo/cache"
|
||||
"github.com/cuigh/auxo/errors"
|
||||
"github.com/cuigh/auxo/log"
|
||||
"github.com/cuigh/auxo/util/lazy"
|
||||
@ -21,16 +23,21 @@ func newVersion(v uint64) swarm.Version {
|
||||
}
|
||||
|
||||
type Docker struct {
|
||||
c *client.Client
|
||||
locker sync.Mutex
|
||||
logger log.Logger
|
||||
agents sync.Map
|
||||
c *client.Client
|
||||
locker sync.Mutex
|
||||
logger log.Logger
|
||||
nodes cache.Value
|
||||
agents sync.Map
|
||||
networks sync.Map
|
||||
}
|
||||
|
||||
func NewDocker() *Docker {
|
||||
return &Docker{
|
||||
d := &Docker{
|
||||
logger: log.Get("docker"),
|
||||
nodes: cache.Value{TTL: 30 * time.Minute},
|
||||
}
|
||||
d.nodes.Load = d.loadCache
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *Docker) call(fn func(c *client.Client) error) error {
|
||||
|
@ -71,16 +71,32 @@ func (d *Docker) NetworkInspect(ctx context.Context, name string) (network types
|
||||
|
||||
// NetworkNames return network names by id list.
|
||||
func (d *Docker) NetworkNames(ctx context.Context, ids ...string) (names map[string]string, err error) {
|
||||
var c *client.Client
|
||||
if c, err = d.client(); err == nil {
|
||||
names = make(map[string]string)
|
||||
for _, id := range ids {
|
||||
var n types.NetworkResource
|
||||
n, err = c.NetworkInspect(ctx, id, types.NetworkInspectOptions{})
|
||||
if err != nil {
|
||||
break
|
||||
var (
|
||||
c *client.Client
|
||||
network types.NetworkResource
|
||||
lookup = func(id string) (n types.NetworkResource, e error) {
|
||||
if c == nil {
|
||||
if c, e = d.client(); e != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
names[id] = n.Name
|
||||
n, e = c.NetworkInspect(ctx, id, types.NetworkInspectOptions{})
|
||||
return
|
||||
}
|
||||
)
|
||||
|
||||
names = make(map[string]string)
|
||||
for _, id := range ids {
|
||||
name, ok := d.networks.Load(id)
|
||||
if ok {
|
||||
names[id] = name.(string)
|
||||
} else {
|
||||
network, err = lookup(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names[id] = network.Name
|
||||
d.networks.Store(id, network.Name)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
@ -3,9 +3,7 @@ package docker
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/cuigh/auxo/cache"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/client"
|
||||
@ -63,13 +61,9 @@ func (d *Docker) NodeInspect(ctx context.Context, id string) (node swarm.Node, r
|
||||
}
|
||||
|
||||
func (d *Docker) NodeMap() (map[string]*Node, error) {
|
||||
v := cache.Value{
|
||||
TTL: 30 * time.Minute,
|
||||
Load: func() (interface{}, error) { return d.loadCache() },
|
||||
}
|
||||
value, err := v.Get(true)
|
||||
nodes, err := d.nodes.Get(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return value.(map[string]*Node), nil
|
||||
return nodes.(map[string]*Node), nil
|
||||
}
|
||||
|
@ -27,32 +27,22 @@ import {
|
||||
NIcon,
|
||||
NButton,
|
||||
} from "naive-ui";
|
||||
import { ref, onMounted, PropType } from "vue";
|
||||
import { ref, onMounted } from "vue";
|
||||
import { CloseOutline, MenuOutline } from "@vicons/ionicons5";
|
||||
import { useResizeObserver } from '@vueuse/core'
|
||||
import { ChartInfo } from "@/api/chart";
|
||||
import { ChartInfo } from "@/api/dashboard";
|
||||
import { createChart } from "./chart";
|
||||
import type { Chart } from "./chart";
|
||||
|
||||
// TS:
|
||||
// interface Props {
|
||||
// msg?: string
|
||||
// labels?: string[]
|
||||
// }
|
||||
// const props = withDefaults(defineProps<Props>(), {
|
||||
// msg: 'hello',
|
||||
// labels: () => ['one', 'two']
|
||||
// })
|
||||
interface Props {
|
||||
info: ChartInfo;
|
||||
data?: any;
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
info: {
|
||||
type: Object as PropType<ChartInfo>,
|
||||
required: true,
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
},
|
||||
})
|
||||
// const props = withDefaults(defineProps<Props>(), {
|
||||
// data: () => null,
|
||||
// })
|
||||
const props = defineProps<Props>()
|
||||
const emits = defineEmits(['remove'])
|
||||
|
||||
const container = ref()
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as echarts from "echarts";
|
||||
import { ChartInfo } from "@/api/chart";
|
||||
import { ChartInfo } from "@/api/dashboard";
|
||||
import { store } from "@/store";
|
||||
|
||||
export abstract class Chart {
|
||||
|
@ -43,7 +43,7 @@
|
||||
<n-form-item-gi :label="t('objects.network', 2)" span="2" path="networks">
|
||||
<n-checkbox-group v-model:value="model.networks">
|
||||
<n-space item-style="display: flex;">
|
||||
<n-checkbox :value="n.id" :label="n.name" v-for="n of networks" />
|
||||
<n-checkbox :value="n.name" :label="n.name" v-for="n of networks" />
|
||||
</n-space>
|
||||
</n-checkbox-group>
|
||||
</n-form-item-gi>
|
||||
@ -653,22 +653,22 @@ function newFile() {
|
||||
}
|
||||
|
||||
async function fetchData() {
|
||||
const name = route.params.name as string || ''
|
||||
if (name) {
|
||||
let r = await serviceApi.find(name);
|
||||
const name = route.params.name as string
|
||||
|
||||
name && serviceApi.find(name, true).then(r => {
|
||||
model.value = r.data?.service as Service;
|
||||
}
|
||||
});
|
||||
|
||||
let nr = await networkApi.search();
|
||||
networks.value = nr.data as Network[];
|
||||
|
||||
let cr = await configApi.search({ pageIndex: 1, pageSize: 1000 });
|
||||
configFiles.value = cr.data?.items.map(c => {
|
||||
let results = await Promise.all([
|
||||
networkApi.search(),
|
||||
configApi.search({ pageIndex: 1, pageSize: 1000 }),
|
||||
secretApi.search({ pageIndex: 1, pageSize: 1000 }),
|
||||
])
|
||||
networks.value = results[0].data?.filter(n => n.name != 'ingress') as Network[];
|
||||
configFiles.value = results[1].data?.items.map(c => {
|
||||
return { label: c.name, value: `${c.id}:${c.name}` }
|
||||
})
|
||||
|
||||
let sr = await secretApi.search({ pageIndex: 1, pageSize: 1000 });
|
||||
secretFiles.value = sr.data?.items.map(c => {
|
||||
secretFiles.value = results[2].data?.items.map(c => {
|
||||
return { label: c.name, value: `${c.id}:${c.name}` }
|
||||
})
|
||||
}
|
||||
|
@ -462,7 +462,7 @@
|
||||
round
|
||||
size="small"
|
||||
v-for="n in t.networks"
|
||||
>{{ n.name + ": " + n.ips.join(',') }}</n-tag>
|
||||
>{{ isEmpty(n.ips) ? n.name : (n.name + ": " + n.ips?.join(',')) }}</n-tag>
|
||||
</n-space>
|
||||
</td>
|
||||
<td>{{ t.updatedAt }}</td>
|
||||
@ -508,7 +508,6 @@ import serviceApi from "@/api/service";
|
||||
import type { Service } from "@/api/service";
|
||||
import taskApi from "@/api/task";
|
||||
import type { Task } from "@/api/task";
|
||||
import networkApi from "@/api/network";
|
||||
import { useRoute } from "vue-router";
|
||||
import { router } from "@/router/router";
|
||||
import { isEmpty } from "@/utils";
|
||||
@ -703,17 +702,11 @@ async function fetchData() {
|
||||
let results = await Promise.all([
|
||||
serviceApi.find(name, true),
|
||||
taskApi.search({ service: name, pageIndex: 1, pageSize: 100 }),
|
||||
networkApi.search(),
|
||||
])
|
||||
|
||||
service.value = results[0].data?.service as Service
|
||||
raw.value = results[0].data?.raw as string;
|
||||
tasks.value = results[1].data?.items as Task[];
|
||||
if (service.value.endpoint.vips && service.value.endpoint.vips.length > 0) {
|
||||
let networks = new Map<string, string>();
|
||||
results[2].data?.forEach(n => networks.set(n.id, n.name))
|
||||
service.value.endpoint.vips.forEach(vip => vip.name = networks.get(vip.id) as string)
|
||||
}
|
||||
cli.value = generateCli(service.value)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user