Correct handling host and ingress networks when editing service

This commit is contained in:
cuigh 2021-12-23 14:40:14 +08:00
parent 63cc555e21
commit 70837391d1
13 changed files with 187 additions and 123 deletions

View File

@ -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.

View File

@ -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 {

View File

@ -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:

View File

@ -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) {

View File

@ -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)

View File

@ -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
}

View File

@ -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 {

View File

@ -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

View File

@ -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
}

View File

@ -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()

View File

@ -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 {

View File

@ -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}` }
})
}

View File

@ -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)
}