github.com/swiftstack/proxyfs@v0.0.0-20201223034610-5434d919416e/httpserver/config.go (about)

     1  package httpserver
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"strconv"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/swiftstack/sortedmap"
    11  
    12  	"github.com/swiftstack/ProxyFS/conf"
    13  	"github.com/swiftstack/ProxyFS/fs"
    14  	"github.com/swiftstack/ProxyFS/headhunter"
    15  	"github.com/swiftstack/ProxyFS/inode"
    16  	"github.com/swiftstack/ProxyFS/transitions"
    17  
    18  	// Force importing of the following next "top-most" packages
    19  	_ "github.com/swiftstack/ProxyFS/fuse"
    20  	_ "github.com/swiftstack/ProxyFS/jrpcfs"
    21  	_ "github.com/swiftstack/ProxyFS/statslogger"
    22  	"github.com/swiftstack/ProxyFS/trackedlock"
    23  )
    24  
    25  type ExtentMapElementStruct struct {
    26  	FileOffset    uint64 `json:"file_offset"`
    27  	ContainerName string `json:"container_name"`
    28  	ObjectName    string `json:"object_name"`
    29  	ObjectOffset  uint64 `json:"object_offset"`
    30  	Length        uint64 `json:"length"`
    31  }
    32  
    33  type jobState uint8
    34  
    35  const (
    36  	jobRunning jobState = iota
    37  	jobHalted
    38  	jobCompleted
    39  )
    40  
    41  type jobTypeType uint8
    42  
    43  const (
    44  	fsckJobType jobTypeType = iota
    45  	scrubJobType
    46  	limitJobType
    47  )
    48  
    49  type jobStruct struct {
    50  	id        uint64
    51  	volume    *volumeStruct
    52  	jobHandle fs.JobHandle
    53  	state     jobState
    54  	startTime time.Time
    55  	endTime   time.Time
    56  }
    57  
    58  // JobStatusJSONPackedStruct describes all the possible fields returned in JSON-encoded job GET body
    59  type JobStatusJSONPackedStruct struct {
    60  	StartTime string   `json:"start time"`
    61  	HaltTime  string   `json:"halt time"`
    62  	DoneTime  string   `json:"done time"`
    63  	ErrorList []string `json:"error list"`
    64  	InfoList  []string `json:"info list"`
    65  }
    66  
    67  type volumeStruct struct {
    68  	trackedlock.Mutex
    69  	name                       string
    70  	fsVolumeHandle             fs.VolumeHandle
    71  	inodeVolumeHandle          inode.VolumeHandle
    72  	headhunterVolumeHandle     headhunter.VolumeHandle
    73  	fsckActiveJob              *jobStruct
    74  	fsckJobs                   sortedmap.LLRBTree // Key == jobStruct.id, Value == *jobStruct
    75  	scrubActiveJob             *jobStruct
    76  	scrubJobs                  sortedmap.LLRBTree // Key == jobStruct.id, Value == *jobStruct
    77  	activeDefragInodeNumberSet map[inode.InodeNumber]struct{}
    78  }
    79  
    80  type globalsStruct struct {
    81  	trackedlock.Mutex
    82  	active            bool
    83  	jobHistoryMaxSize uint32
    84  	whoAmI            string
    85  	ipAddr            string
    86  	tcpPort           uint16
    87  	ipAddrTCPPort     string
    88  	netListener       net.Listener
    89  	wg                sync.WaitGroup
    90  	confMap           conf.ConfMap
    91  	volumeLLRB        sortedmap.LLRBTree // Key == volumeStruct.name, Value == *volumeStruct
    92  }
    93  
    94  var globals globalsStruct
    95  
    96  func init() {
    97  	transitions.Register("httpserver", &globals)
    98  }
    99  
   100  func (dummy *globalsStruct) Up(confMap conf.ConfMap) (err error) {
   101  	globals.volumeLLRB = sortedmap.NewLLRBTree(sortedmap.CompareString, nil)
   102  
   103  	globals.jobHistoryMaxSize, err = confMap.FetchOptionValueUint32("HTTPServer", "JobHistoryMaxSize")
   104  	if nil != err {
   105  		/*
   106  			TODO: Eventually change this to:
   107  				err = fmt.Errorf("confMap.FetchOptionValueString(\"HTTPServer\", \"JobHistoryMaxSize\") failed: %v", err)
   108  				return
   109  		*/
   110  		globals.jobHistoryMaxSize = 5
   111  	}
   112  
   113  	globals.whoAmI, err = confMap.FetchOptionValueString("Cluster", "WhoAmI")
   114  	if nil != err {
   115  		err = fmt.Errorf("confMap.FetchOptionValueString(\"Cluster\", \"WhoAmI\") failed: %v", err)
   116  		return
   117  	}
   118  
   119  	globals.ipAddr, err = confMap.FetchOptionValueString("Peer:"+globals.whoAmI, "PrivateIPAddr")
   120  	if nil != err {
   121  		err = fmt.Errorf("confMap.FetchOptionValueString(\"<whoAmI>\", \"PrivateIPAddr\") failed: %v", err)
   122  		return
   123  	}
   124  
   125  	globals.tcpPort, err = confMap.FetchOptionValueUint16("HTTPServer", "TCPPort")
   126  	if nil != err {
   127  		err = fmt.Errorf("confMap.FetchOptionValueUint16(\"HTTPServer\", \"TCPPort\") failed: %v", err)
   128  		return
   129  	}
   130  
   131  	globals.ipAddrTCPPort = net.JoinHostPort(globals.ipAddr, strconv.Itoa(int(globals.tcpPort)))
   132  
   133  	globals.netListener, err = net.Listen("tcp", globals.ipAddrTCPPort)
   134  	if nil != err {
   135  		err = fmt.Errorf("net.Listen(\"tcp\", \"%s\") failed: %v", globals.ipAddrTCPPort, err)
   136  		return
   137  	}
   138  
   139  	globals.active = false
   140  
   141  	globals.wg.Add(1)
   142  	go serveHTTP()
   143  
   144  	err = nil
   145  	return
   146  }
   147  
   148  func (dummy *globalsStruct) VolumeGroupCreated(confMap conf.ConfMap, volumeGroupName string, activePeer string, virtualIPAddr string) (err error) {
   149  	return nil
   150  }
   151  func (dummy *globalsStruct) VolumeGroupMoved(confMap conf.ConfMap, volumeGroupName string, activePeer string, virtualIPAddr string) (err error) {
   152  	return nil
   153  }
   154  func (dummy *globalsStruct) VolumeGroupDestroyed(confMap conf.ConfMap, volumeGroupName string) (err error) {
   155  	return nil
   156  }
   157  func (dummy *globalsStruct) VolumeCreated(confMap conf.ConfMap, volumeName string, volumeGroupName string) (err error) {
   158  	return nil
   159  }
   160  func (dummy *globalsStruct) VolumeMoved(confMap conf.ConfMap, volumeName string, volumeGroupName string) (err error) {
   161  	return nil
   162  }
   163  func (dummy *globalsStruct) VolumeDestroyed(confMap conf.ConfMap, volumeName string) (err error) {
   164  	return nil
   165  }
   166  
   167  func (dummy *globalsStruct) ServeVolume(confMap conf.ConfMap, volumeName string) (err error) {
   168  	var (
   169  		ok     bool
   170  		volume *volumeStruct
   171  	)
   172  
   173  	volume = &volumeStruct{
   174  		name:                       volumeName,
   175  		fsckActiveJob:              nil,
   176  		fsckJobs:                   sortedmap.NewLLRBTree(sortedmap.CompareUint64, nil),
   177  		scrubActiveJob:             nil,
   178  		scrubJobs:                  sortedmap.NewLLRBTree(sortedmap.CompareUint64, nil),
   179  		activeDefragInodeNumberSet: make(map[inode.InodeNumber]struct{}),
   180  	}
   181  
   182  	volume.fsVolumeHandle, err = fs.FetchVolumeHandleByVolumeName(volume.name)
   183  	if nil != err {
   184  		return
   185  	}
   186  
   187  	volume.inodeVolumeHandle, err = inode.FetchVolumeHandle(volume.name)
   188  	if nil != err {
   189  		return
   190  	}
   191  
   192  	volume.headhunterVolumeHandle, err = headhunter.FetchVolumeHandle(volume.name)
   193  	if nil != err {
   194  		return
   195  	}
   196  
   197  	globals.Lock()
   198  
   199  	ok, err = globals.volumeLLRB.Put(volumeName, volume)
   200  	if nil != err {
   201  		globals.Unlock()
   202  		err = fmt.Errorf("globals.volumeLLRB.Put(%s,) failed: %v", volumeName, err)
   203  		return
   204  	}
   205  	if !ok {
   206  		globals.Unlock()
   207  		err = fmt.Errorf("globals.volumeLLRB.Put(%s,) returned ok == false", volumeName)
   208  		return
   209  	}
   210  
   211  	globals.Unlock()
   212  
   213  	return // return err from globals.volumeLLRB.Put() sufficient
   214  }
   215  
   216  func (dummy *globalsStruct) UnserveVolume(confMap conf.ConfMap, volumeName string) (err error) {
   217  	var (
   218  		ok            bool
   219  		volume        *volumeStruct
   220  		volumeAsValue sortedmap.Value
   221  	)
   222  
   223  	globals.Lock()
   224  
   225  	volumeAsValue, ok, err = globals.volumeLLRB.GetByKey(volumeName)
   226  	if nil != err {
   227  		globals.Unlock()
   228  		err = fmt.Errorf("globals.volumeLLRB.Get(%v) failed: %v", volumeName, err)
   229  		return
   230  	}
   231  
   232  	volume, ok = volumeAsValue.(*volumeStruct)
   233  	if !ok {
   234  		globals.Unlock()
   235  		err = fmt.Errorf("volumeAsValue.(*volumeStruct) returned ok == false for volume %s", volumeName)
   236  		return
   237  	}
   238  
   239  	if !ok {
   240  		globals.Unlock()
   241  		return // return err from globals.volumeLLRB.GetByKey() sufficient
   242  	}
   243  
   244  	volume.Lock()
   245  	if nil != volume.fsckActiveJob {
   246  		volume.fsckActiveJob.jobHandle.Cancel()
   247  		volume.fsckActiveJob.state = jobHalted
   248  		volume.fsckActiveJob.endTime = time.Now()
   249  		volume.fsckActiveJob = nil
   250  	}
   251  	if nil != volume.scrubActiveJob {
   252  		volume.scrubActiveJob.jobHandle.Cancel()
   253  		volume.scrubActiveJob.state = jobHalted
   254  		volume.scrubActiveJob.endTime = time.Now()
   255  		volume.scrubActiveJob = nil
   256  	}
   257  	volume.Unlock()
   258  
   259  	ok, err = globals.volumeLLRB.DeleteByKey(volumeName)
   260  	if nil != err {
   261  		globals.Unlock()
   262  		err = fmt.Errorf("globals.volumeLLRB.DeleteByKey(%v) failed: %v", volumeName, err)
   263  		return
   264  	}
   265  
   266  	globals.Unlock()
   267  
   268  	return // return err from globals.volumeLLRB.DeleteByKey sufficient
   269  }
   270  
   271  func (dummy *globalsStruct) VolumeToBeUnserved(confMap conf.ConfMap, volumeName string) (err error) {
   272  	return nil
   273  }
   274  
   275  func (dummy *globalsStruct) SignaledStart(confMap conf.ConfMap) (err error) {
   276  	globals.confMap = confMap
   277  	globals.active = false
   278  	return nil
   279  }
   280  
   281  func (dummy *globalsStruct) SignaledFinish(confMap conf.ConfMap) (err error) {
   282  	globals.confMap = confMap
   283  	globals.active = true
   284  	return nil
   285  }
   286  
   287  func (dummy *globalsStruct) Down(confMap conf.ConfMap) (err error) {
   288  	var (
   289  		numVolumes int
   290  	)
   291  
   292  	globals.Lock()
   293  
   294  	numVolumes, err = globals.volumeLLRB.Len()
   295  	if nil != err {
   296  		globals.Unlock()
   297  		err = fmt.Errorf("httpserver.Down() couldn't read globals.volumeLLRB: %v", err)
   298  		return
   299  	}
   300  	if 0 != numVolumes {
   301  		globals.Unlock()
   302  		err = fmt.Errorf("httpserver.Down() called with 0 != globals.volumeLLRB.Len()")
   303  		return
   304  	}
   305  
   306  	_ = globals.netListener.Close()
   307  
   308  	globals.Unlock()
   309  
   310  	globals.wg.Wait()
   311  
   312  	err = nil
   313  	return
   314  }