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

implement harbor project day2 configuration #1031

Merged
merged 10 commits into from
May 30, 2023
3 changes: 3 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ resources:
- group: goharbor
kind: HarborCluster
version: v1beta1
- group: goharbor
kind: HarborProject
version: v1beta1
- group: goharbor
kind: HarborServerConfiguration
version: v1alpha1
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ Harbor deployment stack is controlled by a custom Harbor resource `HarborCluster
* Certification auto injection
* Manage Harbor resources with the declaration way
* Robot account
* Project
* and more
* [Auto-scaling](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) for each component.
* Backup/restore data (registry layer, chartmuseum data, databases content).
Expand Down Expand Up @@ -115,6 +114,7 @@ Harbor operator exposes the frontend service with ingress (CRD version: `v1beta1
* [Customize storage, database and cache services](./docs/installation/customize-storage-db-redis.md)
* [Customize images](./docs/customize-images.md)
* [Day2 configurations](docs/day2/day2-configurations.md)
* [Day2 manage Harbor projects](docs/day2/day2-harborprojects.md)
* [Upgrade Harbor cluster](./docs/LCM/upgrade-cluster.md)
* [Delete Harbor cluster](./docs/LCM/cluster-deletion.md)
* [Backup data](./docs/LCM/backup-data.md)
Expand Down
155 changes: 155 additions & 0 deletions apis/goharbor.io/v1beta1/harborproject_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package v1beta1

import (
goyaml "gopkg.in/yaml.v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8syaml "sigs.k8s.io/yaml"
)

// +genclient

// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.

// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +k8s:openapi-gen=true
// +resource:path=harborproject
// +kubebuilder:subresource:status
// +kubebuilder:resource:categories="goharbor",shortName="hp"
// +kubebuilder:printcolumn:name="ProjectName",type=string,JSONPath=`.spec.projectName`,description="Project name in Harbor"
// +kubebuilder:printcolumn:name="HarborServerConfig",type=string,JSONPath=`.spec.harborServerConfig`,description="HarborServerConfiguration name"
thcdrt marked this conversation as resolved.
Show resolved Hide resolved
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.status`,description="HarborProject status"
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`,description="Timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC."
// HarborProject is the Schema for the harbors projects.
type HarborProject struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec HarborProjectSpec `json:"spec,omitempty"`

Status HarborProjectStatus `json:"status,omitempty"`
}

// HarborProjectSpec defines the spec of HarborProject.
type HarborProjectSpec struct {
ChristianLoewel marked this conversation as resolved.
Show resolved Hide resolved
// The name of the harbor project. Has to match harbor's naming rules.
// +kubebuilder:validation:Required
// +kubebuilder:validation:Pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$"
// +kubebuilder:validation:MaxLength=255
// +kubebuilder:validation:MinLength=1
ProjectName string `json:"projectName" yaml:"project_name"`
// The CVE allowlist for the project.
// +kubebuilder:validation:Optional
CveAllowList []string `json:"cveAllowList" yaml:"cve_allow_list_items"`
// The project's storage quota in human-readable format, like in Kubernetes memory requests/limits (Ti, Gi, Mi, Ki). The Harbor's default value is used if empty.
// +kubebuilder:validation:Optional
// +kubebuilder:validation:Pattern="^[1-9][0-9]*(Ti|Gi|Mi|Ki)$"
StorageQuota string `json:"storageQuota" yaml:"storage_quota"`
// HarborProjectMetadata related configurations.
// +kubebuilder:validation:Optional
HarborProjectMetadata *HarborProjectMetadata `json:"metadata" yaml:"metadata"`
// Group or user memberships of the project.
// +kubebuilder:validation:Optional
HarborProjectMemberships []*HarborProjectMember `json:"memberships" yaml:"memberships"`
// HarborServerConfig contains the name of a HarborServerConfig resource describing the harbor instance to manage.
// +kubebuilder:validation:Required
HarborServerConfig string `json:"harborServerConfig"`
}

// ToJSON converts project spec to json payload.
func (h HarborProjectSpec) ToJSON() ([]byte, error) {
data, err := goyaml.Marshal(h)
if err != nil {
return nil, err
}

// convert yaml to json
return k8syaml.YAMLToJSON(data)
}

// HarborProjectMetadata defines the project related metadata.
type HarborProjectMetadata struct {
// Whether content trust is enabled or not. If enabled, user can't pull unsigned images from this project.
// +kubebuilder:validation:Optional
EnableContentTrust *bool `json:"enableContentTrust,omitempty" yaml:"enable_content_trust,omitempty"`
// Whether cosign content trust is enabled or not. Similar to enableContentTrust, but using cosign.
// +kubebuilder:validation:Optional
EnableContentTrustCosign *bool `json:"enableContentTrustCosign,omitempty" yaml:"enable_content_trust_cosign,omitempty"`
// Whether to scan images automatically after pushing.
// +kubebuilder:validation:Optional
AutoScan *bool `json:"autoScan,omitempty" yaml:"auto_scan,omitempty"`
// If an image's vulnerablilities are higher than the severity defined here, the image can't be pulled. Can be either `none`, `low`, `medium`, `high` or `critical`.
// +kubebuilder:validation:Optional
// +kubebuilder:validation:Enum=none;low;medium;high;critical
Severity string `json:"severity,omitempty" yaml:"severity,omitempty"`
// Whether to prevent vulnerable images from running.
// +kubebuilder:validation:Optional
PreventVulnerable *bool `json:"preventVulnerable,omitempty" yaml:"prevent_vulnerable,omitempty"`
// The flag to indicate whether the project should be public or not.
// +kubebuilder:validation:Optional
Public *bool `json:"public,omitempty" yaml:"public,omitempty"`
// Whether this project reuses the system level CVE allowlist for itself. If this is set to `true`, the actual allowlist associated with this project will be ignored.
// +kubebuilder:validation:Optional
ReuseSysCveAllowlist *bool `json:"reuseSysCveAllowlist,omitempty" yaml:"reuse_sys_cve_allowlist,omitempty"`
}

// HarborProjectMember is a member of a HarborProject. Can be a user or group.
type HarborProjectMember struct {
// Type of the member, group or user
// +kubebuilder:validation:Enum="group";"user"
Type string `json:"type" yaml:"type"`
// Name of the member. Has to match with a existing user or group
Name string `json:"name" yaml:"name"`
// Role of the member in the Project. This controls the member's permissions on the project.
// +kubebuilder:validation:Enum="projectAdmin";"developer";"guest";"maintainer"
Role string `json:"role" yaml:"role"`
}

// HarborProjectStatusType defines the status type of project.
type HarborProjectStatusType string

const (
// HarborProjectPhaseReady represents ready status.
HarborProjectStatusReady HarborProjectStatusType = "Success"
// HarborProjectPhaseFail represents fail status.
HarborProjectStatusFail HarborProjectStatusType = "Fail"
// HarborProjectPhaseError represents unknown status.
HarborProjectStatusUnknown HarborProjectStatusType = "Unknown"
)

// HarborProjectStatus defines the status of HarborProject.
type HarborProjectStatus struct {
// Status represents harbor project status.
// +kubebuilder:validation:Optional
Status HarborProjectStatusType `json:"status,omitempty"`
// ProjectID represents ID of the managed project.
// +kubebuilder:validation:Optional
ProjectID int32 `json:"projectID,omitempty"`
// QuotaID is the ID of the project's quota. Used to be able to update it.
// +kubebuilder:validation:Optional
QuotaID int64 `json:"quotaID,omitempty"`
// MembershipHash provides a way to quickly notice changes in project membership.
// +kubebuilder:validation:Optional
MembershipHash string `json:"membershipHash,omitempty"`
// Reason represents status reason.
// +kubebuilder:validation:Optional
Reason string `json:"reason,omitempty"`
// Message provides human-readable message.
// +kubebuilder:validation:Optional
Message string `json:"message,omitempty"`
// LastApplyTime represents the last apply configuration time.
// +kubebuilder:validation:Optional
LastApplyTime *metav1.Time `json:"lastApplyTime,omitempty"`
}

// +kubebuilder:object:root=true
// HarborProjectList contains a list of HarborProjects.
type HarborProjectList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []HarborProject `json:"items"`
}

func init() { //nolint:gochecknoinits
SchemeBuilder.Register(&HarborProject{}, &HarborProjectList{})
}
73 changes: 73 additions & 0 deletions apis/goharbor.io/v1beta1/harborproject_webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package v1beta1

import (
"context"

"github.com/pkg/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
runtime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
ctrl "sigs.k8s.io/controller-runtime"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)

// log is for logging in this package.
var hplog = logf.Log.WithName("harborproject-resource")

func (hp *HarborProject) SetupWebhookWithManager(_ context.Context, mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(hp).
Complete()
}

// +kubebuilder:webhook:verbs=create;update,path=/validate-goharbor-io-v1beta1-harborproject,mutating=false,failurePolicy=fail,groups=goharbor.io,resources=harborprojects,versions=v1beta1,name=vharborproject.kb.io,admissionReviewVersions={"v1beta1","v1"},sideEffects=None

var _ webhook.Validator = &HarborProject{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type.
func (hp *HarborProject) ValidateCreate() error {
hplog.Info("validate create", "name", hp.Name)

return hp.Validate(nil)
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type.
func (hp *HarborProject) ValidateUpdate(old runtime.Object) error {
hplog.Info("validate update", "name", hp.Name)

obj, ok := old.(*HarborProject)
if !ok {
return errors.Errorf("failed type assertion on kind: %s", old.GetObjectKind().GroupVersionKind().String())
}

return hp.Validate(obj)
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type.
func (hp *HarborProject) ValidateDelete() error {
hplog.Info("validate delete", "name", hp.Name)

return nil
}

func (hp *HarborProject) Validate(old *HarborProject) error {
var allErrs field.ErrorList

if old != nil { // update harborproject resource
if hp.Spec.ProjectName != old.Spec.ProjectName {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("projectName"), hp.Spec.ProjectName, "field cannot be changed after initial creation"))
}

if hp.Spec.HarborServerConfig != old.Spec.HarborServerConfig {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("harborServerConfig"), hp.Spec.HarborServerConfig, "field cannot be changed after initial creation"))
}
}

if len(allErrs) == 0 {
return nil
}

return apierrors.NewInvalid(schema.GroupKind{Group: GroupVersion.Group, Kind: "HarborProject"}, hp.Name, allErrs)
}
Loading