Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PC-10133: Move service into separate package #140

Merged
merged 23 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
29ae99d
add validation poc
nieomylnieja Sep 28, 2023
63a33cc
Merge branch 'main' into PC-10133-move-project-into-separate-package
nieomylnieja Sep 28, 2023
396cb2a
further refine the validation example
nieomylnieja Sep 28, 2023
8438ef0
current progress
nieomylnieja Sep 28, 2023
01a89ce
simplify rule logic
nieomylnieja Sep 28, 2023
2492b22
remove v1alpha aliases
nieomylnieja Sep 28, 2023
0795d6b
add errors tests
nieomylnieja Sep 29, 2023
9b71dca
add rules tests
nieomylnieja Sep 29, 2023
60fd200
add strings tests
nieomylnieja Sep 29, 2023
4e3f47c
make sure labels testing is deterministic
nieomylnieja Sep 29, 2023
296068a
invert dependency between project and v1alpha
nieomylnieja Oct 2, 2023
5b582d3
facilitate recent suggestions and feedback
nieomylnieja Oct 2, 2023
760636c
rename object validation to struct
nieomylnieja Oct 2, 2023
d85930b
Merge branch 'main' into PC-10133-move-project-into-separate-package
nieomylnieja Oct 2, 2023
0fadc49
fix error handling
nieomylnieja Oct 3, 2023
4dd7c8a
change labels validation rule name
nieomylnieja Oct 3, 2023
907d1f9
let compiler infer the type
nieomylnieja Oct 4, 2023
3104c3d
declare the function once
nieomylnieja Oct 4, 2023
e2de9a5
move service to separate pkg
nieomylnieja Oct 3, 2023
0c4abe5
extract common validation
nieomylnieja Oct 3, 2023
1ccc047
use common validators
nieomylnieja Oct 4, 2023
6eba209
Merge remote-tracking branch 'origin/main' into PC-10133-move-service…
nieomylnieja Oct 4, 2023
956b8c4
revert changes
nieomylnieja Oct 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions manifest/v1alpha/alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ type AlertMetadata struct {

// AlertSpec represents content of Alert's Spec
type AlertSpec struct {
AlertPolicy AlertPolicyMetadata `json:"alertPolicy"`
SLO SLOMetadata `json:"slo"`
Service ServiceMetadata `json:"service"`
AlertPolicy AlertObjectMetadata `json:"alertPolicy"`
SLO AlertObjectMetadata `json:"slo"`
Service AlertObjectMetadata `json:"service"`
Objective AlertObjective `json:"objective"`
Severity string `json:"severity" validate:"required,severity" example:"High"`
Status string `json:"status" example:"Resolved"`
Expand All @@ -41,3 +41,10 @@ type AlertObjective struct {
Name string `json:"name" validate:"omitempty"`
DisplayName string `json:"displayName" validate:"omitempty"`
}

type AlertObjectMetadata struct {
Name string `json:"name"`
DisplayName string `json:"displayName,omitempty"`
Project string `json:"project,omitempty"`
Labels Labels `json:"labels,omitempty"`
}
3 changes: 2 additions & 1 deletion manifest/v1alpha/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/nobl9/nobl9-go/manifest"
"github.com/nobl9/nobl9-go/manifest/v1alpha"
"github.com/nobl9/nobl9-go/manifest/v1alpha/project"
"github.com/nobl9/nobl9-go/manifest/v1alpha/service"
)

type unmarshalFunc func(v interface{}) error
Expand Down Expand Up @@ -45,7 +46,7 @@ func parseObject(kind manifest.Kind, unmarshal unmarshalFunc) (manifest.Object,
//exhaustive:enforce
switch kind {
case manifest.KindService:
return genericParseObject[v1alpha.Service](unmarshal)
return genericParseObject[service.Service](unmarshal)
case manifest.KindSLO:
return genericParseObject[v1alpha.SLO](unmarshal)
case manifest.KindProject:
Expand Down
20 changes: 10 additions & 10 deletions manifest/v1alpha/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ import (

//go:generate go run ../../../scripts/generate-object-impl.go Project

// New creates a new Project based on provided Metadata nad Spec.
func New(metadata Metadata, spec Spec) Project {
return Project{
APIVersion: manifest.VersionV1alpha.String(),
Kind: manifest.KindProject,
Metadata: metadata,
Spec: spec,
}
}

// Project is the primary grouping primitive for manifest.Object.
// Most objects are scoped to a certain Project.
type Project struct {
Expand All @@ -19,16 +29,6 @@ type Project struct {
ManifestSource string `json:"manifestSrc,omitempty"`
}

// New creates a new Project based on provided Metadata nad Spec.
func New(metadata Metadata, spec Spec) Project {
return Project{
APIVersion: manifest.VersionV1alpha.String(),
Kind: manifest.KindProject,
Metadata: metadata,
Spec: spec,
}
}

// Metadata provides identity information for Project.
type Metadata struct {
Name string `json:"name" validate:"required,objectName" example:"name"`
Expand Down
25 changes: 4 additions & 21 deletions manifest/v1alpha/project/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,10 @@ import (
)

var validateProject = validation.RulesForStruct[Project](
validation.RulesForField(
"metadata.name",
func(p Project) string { return p.Metadata.Name },
).With(
validation.StringRequired(),
validation.StringIsDNSSubdomain()),
validation.RulesForField(
"metadata.displayName",
func(p Project) string { return p.Metadata.DisplayName },
).With(
validation.StringLength(0, 63)),
validation.RulesForField(
"metadata.labels",
func(p Project) v1alpha.Labels { return p.Metadata.Labels },
).With(
v1alpha.ValidationRuleLabels()),
validation.RulesForField(
"spec.description",
func(p Project) string { return p.Spec.Description },
).With(
validation.StringDescription()),
v1alpha.FieldRuleMetadataName(func(p Project) string { return p.Metadata.Name }),
v1alpha.FieldRuleMetadataDisplayName(func(p Project) string { return p.Metadata.DisplayName }),
v1alpha.FieldRuleMetadataLabels(func(p Project) v1alpha.Labels { return p.Metadata.Labels }),
v1alpha.FieldRuleSpecDescription(func(p Project) string { return p.Spec.Description }),
).Validate

func validate(p Project) error {
Expand Down
42 changes: 0 additions & 42 deletions manifest/v1alpha/service.go

This file was deleted.

2 changes: 2 additions & 0 deletions manifest/v1alpha/service/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package service defines Service object definitions.
package service
11 changes: 11 additions & 0 deletions manifest/v1alpha/service/example.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: n9/v1alpha
kind: Service
metadata:
name: my-service
displayName: My Service
project: default
labels:
team: [ green, orange ]
region: [ eu-central-1 ]
spec:
description: Example Service
53 changes: 53 additions & 0 deletions manifest/v1alpha/service/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package service_test

import (
"context"
"log"

"github.com/nobl9/nobl9-go/internal/examples"
"github.com/nobl9/nobl9-go/manifest"
"github.com/nobl9/nobl9-go/manifest/v1alpha"
"github.com/nobl9/nobl9-go/manifest/v1alpha/service"
)

func ExampleService() {
// Create the object:
myService := service.New(
service.Metadata{
Name: "my-service",
DisplayName: "My Service",
Project: "default",
Labels: v1alpha.Labels{
"team": []string{"green", "orange"},
"region": []string{"eu-central-1"},
},
},
service.Spec{
Description: "Example service",
},
)
// Verify the object:
if err := myService.Validate(); err != nil {
log.Fatal("service validation failed, err: %w", err)
}
// Apply the object:
client := examples.GetOfflineEchoClient()
if err := client.ApplyObjects(context.Background(), []manifest.Object{myService}, false); err != nil {
log.Fatal("failed to apply service, err: %w", err)
}
// Output:
// apiVersion: n9/v1alpha
// kind: Service
// metadata:
// name: my-service
// displayName: My Service
// project: default
// labels:
// region:
// - eu-central-1
// team:
// - green
// - orange
// spec:
// description: Example service
}
49 changes: 49 additions & 0 deletions manifest/v1alpha/service/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package service

import (
"github.com/nobl9/nobl9-go/manifest"
"github.com/nobl9/nobl9-go/manifest/v1alpha"
)

//go:generate go run ../../../scripts/generate-object-impl.go Service

// New creates a new Service based on provided Metadata nad Spec.
func New(metadata Metadata, spec Spec) Service {
return Service{
APIVersion: manifest.VersionV1alpha.String(),
Kind: manifest.KindService,
Metadata: metadata,
Spec: spec,
}
}

// Service struct which mapped one to one with kind: service yaml definition
type Service struct {
APIVersion string `json:"apiVersion"`
Kind manifest.Kind `json:"kind"`
Metadata Metadata `json:"metadata"`
Spec Spec `json:"spec"`
Status *Status `json:"status,omitempty"`

Organization string `json:"organization,omitempty"`
ManifestSource string `json:"manifestSrc,omitempty"`
}

// Metadata provides identity information for Service.
type Metadata struct {
Name string `json:"name" validate:"required,objectName"`
DisplayName string `json:"displayName,omitempty" validate:"omitempty,min=0,max=63"`
Project string `json:"project,omitempty" validate:"objectName"`
Labels v1alpha.Labels `json:"labels,omitempty" validate:"omitempty,labels"`
}

// Status holds dynamic fields returned when the Service is fetched from Nobl9 platform.
// Status is not part of the static object definition.
type Status struct {
SloCount int `json:"sloCount"`
}

// Spec holds detailed information specific to Service.
type Spec struct {
Description string `json:"description" validate:"description" example:"Bleeding edge web app"`
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions manifest/v1alpha/service/test_data/expected_error.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Validation for Service 'MY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICE' in project 'MY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECT' has failed for the following fields:
nieomylnieja marked this conversation as resolved.
Show resolved Hide resolved
- 'metadata.name' with value 'MY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICEMY SERVICE...':
- length must be between 1 and 63
- a DNS-1123 compliant name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')
- 'metadata.displayName' with value 'my-servicemy-servicemy-servicemy-servicemy-servicemy-servicemy-servicemy-servicemy-servicemy-service':
- length must be between 0 and 63
- 'metadata.project' with value 'MY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECTMY PROJECT...':
- length must be between 1 and 63
- a DNS-1123 compliant name must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')
- 'metadata.labels' with value '{"L O L":["dip","dip"]}':
- label key 'L O L' does not match the regex: ^\p{L}([_\-0-9\p{L}]*[0-9\p{L}])?$
- 'spec.description' with value 'llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll...':
- length must be between 0 and 1050
Manifest source: /home/me/service.yaml
21 changes: 21 additions & 0 deletions manifest/v1alpha/service/validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package service

import (
"github.com/nobl9/nobl9-go/manifest/v1alpha"
"github.com/nobl9/nobl9-go/validation"
)

var validateService = validation.RulesForStruct[Service](
v1alpha.FieldRuleMetadataName(func(s Service) string { return s.Metadata.Name }),
v1alpha.FieldRuleMetadataDisplayName(func(s Service) string { return s.Metadata.DisplayName }),
v1alpha.FieldRuleMetadataProject(func(s Service) string { return s.Metadata.Project }),
v1alpha.FieldRuleMetadataLabels(func(s Service) v1alpha.Labels { return s.Metadata.Labels }),
v1alpha.FieldRuleSpecDescription(func(s Service) string { return s.Spec.Description }),
).Validate

func validate(s Service) error {
if errs := validateService(s); len(errs) > 0 {
return v1alpha.NewObjectError(s, errs)
}
return nil
}
34 changes: 34 additions & 0 deletions manifest/v1alpha/service/validation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package service

import (
_ "embed"
"strings"
"testing"

"github.com/stretchr/testify/assert"

"github.com/nobl9/nobl9-go/manifest"
"github.com/nobl9/nobl9-go/manifest/v1alpha"
)

//go:embed test_data/expected_error.txt
var expectedError string

func TestValidate_AllErrors(t *testing.T) {
err := validate(Service{
Kind: manifest.KindService,
Metadata: Metadata{
Name: strings.Repeat("MY SERVICE", 20),
DisplayName: strings.Repeat("my-service", 10),
Project: strings.Repeat("MY PROJECT", 20),
Labels: v1alpha.Labels{
"L O L": []string{"dip", "dip"},
},
},
Spec: Spec{
Description: strings.Repeat("l", 2000),
},
ManifestSource: "/home/me/service.yaml",
})
assert.Equal(t, expectedError, err.Error())
}
28 changes: 28 additions & 0 deletions manifest/v1alpha/validation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package v1alpha

import "github.com/nobl9/nobl9-go/validation"

func FieldRuleMetadataName[S any](getter func(S) string) validation.FieldRules[string, S] {
return validation.RulesForField[string]("metadata.name", getter).
With(validation.StringRequired(), validation.StringIsDNSSubdomain())
}

func FieldRuleMetadataDisplayName[S any](getter func(S) string) validation.FieldRules[string, S] {
return validation.RulesForField[string]("metadata.displayName", getter).
With(validation.StringLength(0, 63))
}

func FieldRuleMetadataProject[S any](getter func(S) string) validation.FieldRules[string, S] {
return validation.RulesForField[string]("metadata.project", getter).
With(validation.StringRequired(), validation.StringIsDNSSubdomain())
}

func FieldRuleMetadataLabels[S any](getter func(S) Labels) validation.FieldRules[Labels, S] {
return validation.RulesForField[Labels]("metadata.labels", getter).
With(ValidationRuleLabels())
}

func FieldRuleSpecDescription[S any](getter func(S) string) validation.FieldRules[string, S] {
return validation.RulesForField[string]("spec.description", getter).
With(validation.StringDescription())
}
Loading
Loading