Copyright (c) 2024 Huawei Technologies Co., Ltd.
openFuyao is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/
package checker
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"time"
"openfuyao.com/oscheck/internal/conf"
"openfuyao.com/oscheck/internal/utils"
)
type ResultType int
const (
ResultValid ResultType = iota
ResultInvalid
ResultError
)
type Context struct {
RootPath string
}
func (t ResultType) MarshalText() ([]byte, error) {
var text string
switch t {
case ResultError:
text = "error"
case ResultInvalid:
text = "failed"
case ResultValid:
text = "ok"
default:
return []byte{}, fmt.Errorf("failed to marshal %v for CheckRstType", t)
}
return []byte(text), nil
}
func (t *ResultType) UnmarshalText(text []byte) error {
var value ResultType
switch string(text) {
case "error":
value = ResultError
case "failed":
value = ResultInvalid
case "ok":
value = ResultValid
default:
return fmt.Errorf("failed to parse %s for CheckRstType", text)
}
*t = value
return nil
}
const (
matchLast = "last"
matchFirst = "first"
matchOnce = "once"
)
type SubItemParamBase struct {
Name string `json:"name"`
Key string `json:"key"`
Expect interface{} `json:"expect"`
Type string `json:"type"`
Doc string `json:"doc"`
Default interface{} `json:"default"`
MatchType string `json:"matchType"`
}
func (p *SubItemParamBase) Init() error {
if !checkParamFormat(*p) {
return fmt.Errorf("param format error")
}
return nil
}
func getForJsonNumber(maybeNumber interface{}) interface{} {
jsoNum, ok := maybeNumber.(json.Number)
if ok {
i64v, err := jsoNum.Int64()
if err == nil {
return i64v
} else {
f64v, err := jsoNum.Float64()
if err == nil {
return f64v
}
}
}
if str, ok := maybeNumber.(string); ok {
if num, err := utils.ParseInt10Base(str); err == nil {
return num
}
}
return maybeNumber
}
func (p *SubItemParamBase) UnmarshalJSON(b []byte) error {
type Alias SubItemParamBase
tmp := Alias{}
decoder := json.NewDecoder(bytes.NewReader(b))
decoder.UseNumber()
if err := decoder.Decode(&tmp); err != nil {
return err
}
p.Name = tmp.Name
p.Expect = getForJsonNumber(tmp.Expect)
p.Type = tmp.Type
p.Doc = tmp.Doc
p.Key = tmp.Key
p.MatchType = strings.TrimSpace(tmp.MatchType)
if p.MatchType == "" {
p.MatchType = matchLast
}
if tmp.Default == nil {
p.Default = ""
} else {
p.Default = getForJsonNumber(tmp.Default)
}
return nil
}
type ParamCheckError struct {
ErrField []string
Msg string
FormatErr bool
}
func (e ParamCheckError) Error() string {
return e.Msg
}
func checkParamFormat(param SubItemParamBase) bool {
rslt := len(param.Name) > 0
rslt = rslt && param.Type != ""
rslt = rslt && (param.MatchType == matchLast || param.MatchType == matchFirst || param.MatchType == matchOnce)
return rslt
}
type Result struct {
HostName string `yaml:"hostName" json:"hostName"`
CheckSet string `yaml:"checkSet" json:"checkSet"`
Result ResultType `yaml:"result" json:"result"`
StartTime time.Time `yaml:"startTime" json:"startTime"`
EndTime time.Time `yaml:"endTime" json:"endTime"`
Items []ItemRslt `yaml:"items,omitempty" json:"items,omitempty"`
}
type ItemRslt struct {
Key string `yaml:"key" json:"key"`
Result ResultType `yaml:"result" json:"result"`
Doc string `yaml:"doc" json:"doc"`
SubItems []SubItemRslt `yaml:"subItems,omitempty" json:"subItems,omitempty"`
}
type SubItemRslt struct {
Key string `yaml:"key" json:"key"`
Expect string `yaml:"expect" json:"expect"`
Real string `yaml:"real" json:"real"`
Result ResultType `yaml:"result" json:"result"`
Doc string `yaml:"doc" json:"doc"`
}
type Checker interface {
Init(ctx Context, item conf.CheckItem) *ParamCheckError
Check() ItemRslt
}
var checkFactories map[string]func() Checker = make(map[string]func() Checker)
func RegisterCheckerFactory(checkerType string, factory func() Checker) {
checkFactories[checkerType] = factory
}
func GetChecker(checkerType string) (Checker, error) {
factory, ok := checkFactories[checkerType]
if !ok {
return nil, fmt.Errorf("checker type %s not found", checkerType)
}
checker := factory()
if checker == nil {
return nil, fmt.Errorf("checker type %s factory returned nil", checkerType)
}
return checker, nil
}
func matchValue(matchType string, checker ValueChecker, values []string) bool {
switch matchType {
case matchFirst:
return checker.Check(values[0])
case matchLast:
return checker.Check(values[len(values)-1])
case matchOnce:
for _, value := range values {
if checker.Check(value) {
return true
}
}
default:
return false
}
return false
}
func convertSpec2Special(checkItem conf.CheckItem, spec interface{}) error {
content, err := json.Marshal(checkItem.Spec)
if err != nil {
return err
}
err = json.Unmarshal(content, spec)
if err != nil {
return err
}
return nil
}