Skip to content

Commit

Permalink
Provisioning: Add resource types (grafana#98727)
Browse files Browse the repository at this point in the history
Co-authored-by: Mariell Hoversholm <[email protected]>
Co-authored-by: Roberto Jimenez Sanchez <[email protected]>
  • Loading branch information
3 people authored Jan 9, 2025
1 parent 56be39e commit a84ab52
Show file tree
Hide file tree
Showing 38 changed files with 3,882 additions and 11 deletions.
22 changes: 22 additions & 0 deletions pkg/apis/provisioning/v0alpha1/classic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package v0alpha1

// File types used from classic provisioning
// +enum
type ClassicFileType string

const (
// Dashboard JSON
ClassicDashboard ClassicFileType = "dashboard"

// Datasource definitions
// eg: https://github.com/grafana/grafana/blob/v11.3.1/conf/provisioning/datasources/sample.yaml
ClassicDatasources ClassicFileType = "datasources"

// Alert configuration
// https://github.com/grafana/grafana/blob/v11.3.1/conf/provisioning/alerting/sample.yaml
ClassicAlerting ClassicFileType = "alerting"

// Access control
// https://github.com/grafana/grafana/blob/v11.3.1/conf/provisioning/access-control/sample.yaml
ClassicAccessControl ClassicFileType = "access-control"
)
6 changes: 6 additions & 0 deletions pkg/apis/provisioning/v0alpha1/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// +k8s:deepcopy-gen=package
// +k8s:openapi-gen=true
// +k8s:defaulter-gen=TypeMeta
// +groupName=provisioning.grafana.app

package v0alpha1 // import "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
98 changes: 98 additions & 0 deletions pkg/apis/provisioning/v0alpha1/jobs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package v0alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// The repository name and type are stored as labels
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type Job struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec JobSpec `json:"spec,omitempty"`
Status JobStatus `json:"status,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type JobList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`

Items []Job `json:"items,omitempty"`
}

// +enum
type JobAction string

const (
// Update a pull request -- send preview images, links etc
JobActionPullRequest JobAction = "pr"

// Sync the remote branch with the grafana instance
JobActionSync JobAction = "sync"

// Export from grafana into the remote repository
JobActionExport JobAction = "export"
)

// +enum
type JobState string

const (
// Job has been submitted, but not processed yet
JobStatePending JobState = "pending"

// The job is running
JobStateWorking JobState = "working"

// Finished with success
JobStateSuccess JobState = "success"

// Finished with errors
JobStateError JobState = "error"
)

func (j JobState) Finished() bool {
return j == JobStateSuccess || j == JobStateError
}

type JobSpec struct {
Action JobAction `json:"action"`

// The branch of commit hash
Ref string `json:"ref,omitempty"`

// Pull request number (when appropriate)
PR int `json:"pr,omitempty"`
Hash string `json:"hash,omitempty"` // used in PR code... not sure it is necessary

// URL to the originator (eg, PR URL)
URL string `json:"url,omitempty"`
}

// The job status
type JobStatus struct {
State JobState `json:"state,omitempty"`
Started int64 `json:"started,omitempty"`
Finished int64 `json:"finished,omitempty"`
Message string `json:"message,omitempty"`
Errors []string `json:"errors,omitempty"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type WebhookResponse struct {
metav1.TypeMeta `json:",inline"`

// HTTP Status code
// 200 implies that the payload was understood but nothing is required
// 202 implies that an async job has been scheduled to handle the request
Code int `json:"code,omitempty"`

// Optional message
Message string `json:"added,omitempty"`

// Jobs to be processed
// When the response is 202 (Accepted) the queued jobs will be returned
Job *JobSpec `json:"job,omitempty"`
}
129 changes: 129 additions & 0 deletions pkg/apis/provisioning/v0alpha1/register.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package v0alpha1

import (
"errors"
"fmt"
"time"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"

"github.com/grafana/grafana/pkg/apimachinery/utils"
)

const (
GROUP = "provisioning.grafana.app"
VERSION = "v0alpha1"
APIVERSION = GROUP + "/" + VERSION
)

var RepositoryResourceInfo = utils.NewResourceInfo(GROUP, VERSION,
"repositories", "repository", "Repositories",
func() runtime.Object { return &Repository{} }, // newObj
func() runtime.Object { return &RepositoryList{} }, // newList
utils.TableColumns{ // Returned by `kubectl get`. Doesn't affect disk storage.
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Created At", Type: "date"},
{Name: "Title", Type: "string"},
{Name: "Type", Type: "string"},
{Name: "Target", Type: "string"},
},
Reader: func(obj any) ([]interface{}, error) {
m, ok := obj.(*Repository)
if !ok {
return nil, errors.New("expected Repository")
}

var target string
switch m.Spec.Type {
case LocalRepositoryType:
target = m.Spec.Local.Path
case S3RepositoryType:
target = m.Spec.S3.Bucket
case GitHubRepositoryType:
target = fmt.Sprintf("%s/%s", m.Spec.GitHub.Owner, m.Spec.GitHub.Repository)
}

return []interface{}{
m.Name, // may our may not be nice to read
m.CreationTimestamp.UTC().Format(time.RFC3339),
m.Spec.Title, // explicitly configured title that can change
m.Spec.Type,
target,
}, nil
},
})

var JobResourceInfo = utils.NewResourceInfo(GROUP, VERSION,
"jobs", "job", "Job",
func() runtime.Object { return &Job{} }, // newObj
func() runtime.Object { return &JobList{} }, // newList
utils.TableColumns{ // Returned by `kubectl get`. Doesn't affect disk storage.
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Created At", Type: "date"},
{Name: "Action", Type: "string"},
{Name: "State", Type: "string"},
{Name: "Repository", Type: "string"},
},
Reader: func(obj any) ([]interface{}, error) {
m, ok := obj.(*Job)
if !ok {
return nil, errors.New("expected Repository")
}

return []interface{}{
m.Name, // may our may not be nice to read
m.CreationTimestamp.UTC().Format(time.RFC3339),
m.Spec.Action,
m.Status.State,
m.Labels["repository"],
}, nil
},
})

var (
// SchemeGroupVersion is group version used to register these objects
SchemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION}
InternalGroupVersion = schema.GroupVersion{Group: GROUP, Version: runtime.APIVersionInternal}

// SchemaBuilder is used by standard codegen
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)

func init() {
localSchemeBuilder.Register(func(s *runtime.Scheme) error {
err := AddKnownTypes(SchemeGroupVersion, s)
if err != nil {
return err
}
metav1.AddToGroupVersion(s, SchemeGroupVersion)
return nil
})
}

// Adds the list of known types to the given scheme.
func AddKnownTypes(gv schema.GroupVersion, scheme *runtime.Scheme) error {
scheme.AddKnownTypes(gv,
&Repository{},
&RepositoryList{},
&HelloWorld{},
&WebhookResponse{},
&ResourceWrapper{},
&FileList{},
&HistoryList{},
&TestResults{},
&Job{},
&JobList{},
)
return nil
}

// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
Loading

0 comments on commit a84ab52

Please sign in to comment.