~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
209 lines · 3.8 KB Raw History
package component

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"sync"

	commonv1 "github.com/johnandersen777/socialweb-computer-kcp/api/common/v1alpha1"
)

const (
	ProtocolHTTP  = "http"
	ProtocolHTTPS = "https"
	ProtocolWS    = "ws"
	ProtocolWSS   = "wss"
	ProtocolTCP   = "tcp"
	ProtocolSSH   = "ssh"
)

type Key struct {
	Workspace string
	Kind      string
	Name      string
}

func (k Key) Ref() commonv1.ObjectRef {
	return commonv1.ObjectRef{Workspace: k.Workspace, Kind: k.Kind, Name: k.Name}
}

func KeyOf(ref commonv1.ObjectRef) Key {
	return Key{Workspace: strings.TrimSpace(ref.Workspace), Kind: strings.TrimSpace(ref.Kind), Name: strings.TrimSpace(ref.Name)}
}

type Endpoint struct {
	Name     string
	Protocol string
	URL      string
	Address  string
}

func PrimaryEndpoint(endpoints []Endpoint) (Endpoint, bool) {
	for _, endpoint := range endpoints {
		if endpoint.Name == ProtocolHTTP {
			return endpoint, true
		}
	}
	for _, endpoint := range endpoints {
		if endpoint.URL != "" {
			return endpoint, true
		}
	}
	if len(endpoints) > 0 {
		return endpoints[0], true
	}
	return Endpoint{}, false
}

type Service interface {
	Start(ctx context.Context) error
	Ready() Readiness
	Endpoints() []Endpoint
	Stop(ctx context.Context) error
}

type Readiness interface {
	Done() <-chan struct{}
	Err() error
}

var ErrNotReady = errors.New("component: the service failed to become ready")

type ReadySignal struct {
	mu   sync.Mutex
	done chan struct{}
	err  error
	set  bool
}

var _ Readiness = (*ReadySignal)(nil)

func (r *ReadySignal) Done() <-chan struct{} {
	r.mu.Lock()
	defer r.mu.Unlock()
	return r.doneLocked()
}

func (r *ReadySignal) Err() error {
	r.mu.Lock()
	defer r.mu.Unlock()
	return r.err
}

func (r *ReadySignal) Signal() {
	r.resolve(nil)
}

func (r *ReadySignal) Fail(err error) {
	if err == nil {
		err = ErrNotReady
	}
	r.resolve(err)
}

func (r *ReadySignal) resolve(err error) {
	r.mu.Lock()
	defer r.mu.Unlock()
	if r.set {
		return
	}
	r.set = true
	r.err = err
	close(r.doneLocked())
}

func (r *ReadySignal) doneLocked() chan struct{} {
	if r.done == nil {
		r.done = make(chan struct{})
	}
	return r.done
}

type NetworkHosts interface {
	Address(hostport string) (string, bool)
	Hosts() map[string]string
}

type RawSpec json.RawMessage

func (s RawSpec) Bytes() []byte {
	return []byte(s)
}

func (s RawSpec) Decode(v any) error {
	if len(s) == 0 {
		return nil
	}
	if err := json.Unmarshal(s, v); err != nil {
		return fmt.Errorf("component: decoding the raw spec: %w", err)
	}
	return nil
}

func (s RawSpec) Map() (map[string]any, error) {
	out := map[string]any{}
	if err := s.Decode(&out); err != nil {
		return nil, err
	}
	if out == nil {
		out = map[string]any{}
	}
	return out, nil
}

func (s RawSpec) MarshalJSON() ([]byte, error) {
	if len(s) == 0 {
		return []byte("null"), nil
	}
	if !json.Valid(s) {
		return nil, errors.New("component: the raw spec is not valid JSON")
	}
	return s, nil
}

func (s *RawSpec) UnmarshalJSON(b []byte) error {
	if s == nil {
		return errors.New("component: raw spec unmarshal into nil")
	}
	*s = append((*s)[:0], b...)
	return nil
}

type RestartPolicy string

const (
	RestartAlways    RestartPolicy = "Always"
	RestartOnFailure RestartPolicy = "OnFailure"
	RestartNever     RestartPolicy = "Never"
)

func (p RestartPolicy) Valid() bool {
	switch p {
	case RestartAlways, RestartOnFailure, RestartNever:
		return true
	default:
		return false
	}
}

func (p RestartPolicy) WithDefault() RestartPolicy {
	if p.Valid() {
		return p
	}
	return RestartAlways
}

type Phase string

const (
	PhasePending   Phase = "Pending"
	PhaseStarting  Phase = "Starting"
	PhaseRunning   Phase = "Running"
	PhaseReady     Phase = "Ready"
	PhaseFailed    Phase = "Failed"
	PhaseStopping  Phase = "Stopping"
	PhaseSuspended Phase = "Suspended"
)