216 lines
4.4 KiB
Go
216 lines
4.4 KiB
Go
/*
|
|
* Copyright 2020 The Dragonfly Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package config
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
logger "d7y.io/dragonfly/v2/internal/dflog"
|
|
"d7y.io/dragonfly/v2/internal/dfpath"
|
|
dc "d7y.io/dragonfly/v2/internal/dynconfig"
|
|
"d7y.io/dragonfly/v2/internal/rpc/manager"
|
|
"d7y.io/dragonfly/v2/pkg/util/net/iputils"
|
|
)
|
|
|
|
var (
|
|
SchedulerDynconfigPath = filepath.Join(dfpath.WorkHome, "dynconfig/scheduler.json")
|
|
SchedulerDynconfigCachePath = filepath.Join(dfpath.WorkHome, "dynconfig/scheduler")
|
|
)
|
|
|
|
var (
|
|
watchInterval = 1 * time.Second
|
|
)
|
|
|
|
type DynconfigInterface interface {
|
|
// Get the dynamic config from manager.
|
|
Get() (*manager.Scheduler, error)
|
|
|
|
// Register allows an instance to register itself to listen/observe events.
|
|
Register(Observer)
|
|
|
|
// Deregister allows an instance to remove itself from the collection of observers/listeners.
|
|
Deregister(Observer)
|
|
|
|
// Notify publishes new events to listeners.
|
|
Notify() error
|
|
|
|
// Serve the dynconfig listening service.
|
|
Serve() error
|
|
|
|
// Stop the dynconfig listening service.
|
|
Stop()
|
|
}
|
|
|
|
type Observer interface {
|
|
// OnNotify allows an event to be "published" to interface implementations.
|
|
OnNotify(*manager.Scheduler)
|
|
}
|
|
|
|
type dynconfig struct {
|
|
*dc.Dynconfig
|
|
observers map[Observer]struct{}
|
|
done chan bool
|
|
cdnDirPath string
|
|
}
|
|
|
|
// TODO(Gaius) Rely on manager to delete cdnDirPath
|
|
func NewDynconfig(sourceType dc.SourceType, cdnDirPath string, options ...dc.Option) (DynconfigInterface, error) {
|
|
d := &dynconfig{
|
|
observers: map[Observer]struct{}{},
|
|
done: make(chan bool),
|
|
cdnDirPath: cdnDirPath,
|
|
}
|
|
|
|
client, err := dc.New(sourceType, options...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
d.Dynconfig = client
|
|
return d, nil
|
|
}
|
|
|
|
func (d *dynconfig) Get() (*manager.Scheduler, error) {
|
|
var config manager.Scheduler
|
|
if d.cdnDirPath != "" {
|
|
cdn, err := d.getCDNFromDirPath()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
config.Cdns = cdn
|
|
} else {
|
|
if err := d.Unmarshal(&config); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return &config, nil
|
|
}
|
|
|
|
func (d *dynconfig) getCDNFromDirPath() ([]*manager.CDN, error) {
|
|
files, err := ioutil.ReadDir(d.cdnDirPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var data []*manager.CDN
|
|
for _, file := range files {
|
|
// skip directory
|
|
if file.IsDir() {
|
|
continue
|
|
}
|
|
|
|
p := filepath.Join(d.cdnDirPath, file.Name())
|
|
if file.Mode()&os.ModeSymlink != 0 {
|
|
stat, err := os.Stat(p)
|
|
if err != nil {
|
|
logger.Errorf("stat %s error: %s", file.Name(), err)
|
|
continue
|
|
}
|
|
// skip symbol link directory
|
|
if stat.IsDir() {
|
|
continue
|
|
}
|
|
}
|
|
b, err := ioutil.ReadFile(p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var s *manager.CDN
|
|
if err := json.Unmarshal(b, &s); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
data = append(data, s)
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
type managerClient struct {
|
|
manager.ManagerClient
|
|
}
|
|
|
|
func (d *dynconfig) Register(l Observer) {
|
|
d.observers[l] = struct{}{}
|
|
}
|
|
|
|
func (d *dynconfig) Deregister(l Observer) {
|
|
delete(d.observers, l)
|
|
}
|
|
|
|
func (d *dynconfig) Notify() error {
|
|
config, err := d.Get()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for o := range d.observers {
|
|
o.OnNotify(config)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *dynconfig) Serve() error {
|
|
if err := d.Notify(); err != nil {
|
|
return err
|
|
}
|
|
|
|
go d.watch()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *dynconfig) watch() {
|
|
tick := time.NewTicker(watchInterval)
|
|
|
|
for {
|
|
select {
|
|
case <-tick.C:
|
|
d.Notify()
|
|
case <-d.done:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d *dynconfig) Stop() {
|
|
close(d.done)
|
|
}
|
|
|
|
func NewManagerClient(client manager.ManagerClient) dc.ManagerClient {
|
|
return &managerClient{client}
|
|
}
|
|
|
|
func (mc *managerClient) Get() (interface{}, error) {
|
|
scConfig, err := mc.GetScheduler(context.Background(), &manager.GetSchedulerRequest{
|
|
HostName: iputils.HostName,
|
|
SourceType: manager.SourceType_SCHEDULER_SOURCE,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return scConfig, nil
|
|
}
|