define helmapp.rainbond.goodrain.io

This commit is contained in:
GLYASAI 2021-04-14 20:17:40 +08:00
parent e684e7b6b1
commit 50661123f1
16 changed files with 629 additions and 25 deletions

View File

@ -47,4 +47,32 @@ mock:
help: ## this help
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
precommit:
./precheck.sh
./precheck.sh
# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
CRD_OPTIONS ?= "crd:trivialVersions=true"
# Generate manifests e.g. CRD, RBAC etc.
manifests: controller-gen
$(CONTROLLER_GEN) $(CRD_OPTIONS) paths="./worker/..." output:crd:artifacts:config=config/crd
# Generate code
generate: controller-gen
$(CONTROLLER_GEN) object:headerFile="hack/k8s/codegen/boilerplate.go.txt" paths="./worker/api/..."
# find or download controller-gen
# download controller-gen if necessary
controller-gen:
ifeq (, $(shell which controller-gen))
@{ \
set -e ;\
CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\
cd $$CONTROLLER_GEN_TMP_DIR ;\
go mod init tmp ;\
go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.2.5 ;\
rm -rf $$CONTROLLER_GEN_TMP_DIR ;\
}
CONTROLLER_GEN=$(GOBIN)/controller-gen
else
CONTROLLER_GEN=$(shell which controller-gen)
endif

View File

@ -8,6 +8,7 @@ import (
"github.com/goodrain/rainbond/api/handler"
"github.com/goodrain/rainbond/api/middleware"
"github.com/goodrain/rainbond/api/model"
"github.com/goodrain/rainbond/api/util/bcode"
dbmodel "github.com/goodrain/rainbond/db/model"
httputil "github.com/goodrain/rainbond/util/http"
)
@ -21,6 +22,20 @@ func (a *ApplicationController) CreateApp(w http.ResponseWriter, r *http.Request
if !httputil.ValidatorRequestStructAndErrorResponse(r, w, &tenantReq, nil) {
return
}
if tenantReq.AppType == model.AppTypeHelm {
if tenantReq.AppStoreName == "" {
httputil.ReturnBcodeError(r, w, bcode.NewBadRequest("the field 'app_tore_name' is required"))
return
}
if tenantReq.HelmAppName == "" {
httputil.ReturnBcodeError(r, w, bcode.NewBadRequest("the field 'helm_app_name' is required"))
return
}
if tenantReq.Version == "" {
httputil.ReturnBcodeError(r, w, bcode.NewBadRequest("the field 'version' is required"))
return
}
}
// get current tenant
tenant := r.Context().Value(middleware.ContextKey("tenant")).(*dbmodel.Tenants)

View File

@ -14,6 +14,7 @@ import (
"github.com/goodrain/rainbond/util"
"github.com/goodrain/rainbond/worker/client"
"github.com/goodrain/rainbond/worker/server/pb"
"github.com/jinzhu/gorm"
"github.com/sirupsen/logrus"
)
@ -54,36 +55,33 @@ func NewApplicationHandler(statusCli *client.AppRuntimeSyncClient, promClient pr
// CreateApp -
func (a *ApplicationAction) CreateApp(req *model.Application) (*model.Application, error) {
appReq := &dbmodel.Application{
AppName: req.AppName,
AppID: util.NewUUID(),
TenantID: req.TenantID,
EID: req.EID,
TenantID: req.TenantID,
AppID: util.NewUUID(),
AppName: req.AppName,
AppType: req.AppType,
AppStoreName: req.AppStoreName,
HelmAppName: req.HelmAppName,
Version: req.Version,
}
req.AppID = appReq.AppID
tx := db.GetManager().Begin()
defer func() {
if r := recover(); r != nil {
logrus.Errorf("Unexpected panic occurred, rollback transaction: %v", r)
tx.Rollback()
err := tx.Transaction(func(tx *gorm.DB) error {
if err := db.GetManager().ApplicationDaoTransactions(tx).AddModel(appReq); err != nil {
return err
}
}()
if err := db.GetManager().ApplicationDao().AddModel(appReq); err != nil {
tx.Rollback()
return nil, err
}
if len(req.ServiceIDs) != 0 {
if err := db.GetManager().TenantServiceDao().BindAppByServiceIDs(appReq.AppID, req.ServiceIDs); err != nil {
tx.Rollback()
return nil, err
if len(req.ServiceIDs) != 0 {
if err := db.GetManager().TenantServiceDaoTransactions(tx).BindAppByServiceIDs(appReq.AppID, req.ServiceIDs); err != nil {
return err
}
}
}
if err := tx.Commit().Error; err != nil {
tx.Rollback()
return nil, err
}
return req, nil
// create app.rainbond.io
return nil
})
return req, err
}
// BatchCreateApp -

View File

@ -25,6 +25,12 @@ import (
dbmodel "github.com/goodrain/rainbond/db/model"
)
// AppType
const (
AppTypeRainbond = "rainbond"
AppTypeHelm = "helm"
)
//ServiceGetCommon path参数
//swagger:parameters getVolumes getDepVolumes
type ServiceGetCommon struct {
@ -1643,11 +1649,16 @@ func NewAppStatusFromImport(app *ImportAppStruct) *dbmodel.AppStatus {
// Application -
type Application struct {
EID string `json:"eid" validate:"required"`
AppName string `json:"app_name" validate:"required"`
AppType string `json:"app_type" validate:"required,oneof=rainbond helm"`
ConsoleAppID int64 `json:"console_app_id"`
AppID string `json:"app_id"`
TenantID string `json:"tenant_id"`
ServiceIDs []string `json:"service_ids"`
AppStoreName string `json:"app_store_name"`
HelmAppName string `json:"helm_app_name"`
Version string `json:"version"`
}
// CreateAppRequest -

View File

@ -0,0 +1,117 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.2.5
creationTimestamp: null
name: helmapps.rainbond.goodrdain.io
spec:
group: rainbond.goodrdain.io
names:
kind: HelmApp
listKind: HelmAppList
plural: helmapps
singular: helmapp
scope: Namespaced
validation:
openAPIV3Schema:
description: HelmApp is the Schema for the helmapps API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: HelmAppSpec defines the desired state of HelmApp
properties:
appName:
description: 'The application name. TODO: validation'
type: string
preStatus:
description: The prerequisite status.
enum:
- NotConfigured
- Configured
type: string
revision:
description: The application revision.
format: int32
type: integer
values:
description: The values.yaml of the helm app, encoded by base64.
type: string
version:
description: 'The application version. TODO: validation'
type: string
required:
- appName
- revision
- values
- version
type: object
status:
description: HelmAppStatus defines the observed state of HelmApp
properties:
conditions:
description: Current state of helm app.
items:
description: HelmAppCondition contains details for the current condition
of this helm application.
properties:
lastTransitionTime:
description: Last time the condition transitioned from one status
to another.
format: date-time
type: string
message:
description: Human-readable message indicating details about last
transition.
type: string
reason:
description: Unique, one-word, CamelCase reason for the condition's
last transition.
type: string
status:
description: 'Status is the status of the condition. Can be True,
False, Unknown. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions'
type: string
type:
description: Type is the type of the condition.
type: string
required:
- status
- type
type: object
type: array
currentRevision:
type: string
currentValues:
type: string
status:
description: The status of helm app.
type: string
required:
- status
type: object
type: object
version: v1alpha1
versions:
- name: v1alpha1
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@ -39,6 +39,7 @@ type Manager interface {
LicenseDao() dao.LicenseDao
AppDao() dao.AppDao
ApplicationDao() dao.ApplicationDao
ApplicationDaoTransactions(db *gorm.DB) dao.ApplicationDao
AppConfigGroupDao() dao.AppConfigGroupDao
AppConfigGroupDaoTransactions(db *gorm.DB) dao.AppConfigGroupDao
AppConfigGroupServiceDao() dao.AppConfigGroupServiceDao

View File

@ -15,9 +15,14 @@ func IsGovernanceModeValid(governanceMode string) bool {
// Application -
type Application struct {
Model
EID string `gorm:"column:eid" json:"eid"`
TenantID string `gorm:"column:tenant_id" json:"tenant_id"`
AppName string `gorm:"column:app_name" json:"app_name"`
AppID string `gorm:"column:app_id" json:"app_id"`
TenantID string `gorm:"column:tenant_id" json:"tenant_id"`
AppType string `gorm:"column:app_type;default:rainbond" json:"app_type"`
AppStoreName string `gorm:"column:app_store_name" json:"app_store_name"`
HelmAppName string `gorm:"column:helm_app_name" json:"helm_app_name"`
Version string `gorm:"column:Version" json:"Version"`
GovernanceMode string `gorm:"column:governance_mode;default:'BUILD_IN_SERVICE_MESH'" json:"governance_mode"`
}

View File

@ -424,6 +424,13 @@ func (m *Manager) ApplicationDao() dao.ApplicationDao {
}
}
//ApplicationDaoTransactions -
func (m *Manager) ApplicationDaoTransactions(db *gorm.DB) dao.ApplicationDao {
return &mysqldao.ApplicationDaoImpl{
DB: db,
}
}
// AppConfigGroupDao -
func (m *Manager) AppConfigGroupDao() dao.AppConfigGroupDao {
return &mysqldao.AppConfigGroupDaoImpl{

1
go.mod
View File

@ -92,6 +92,7 @@ require (
k8s.io/client-go v12.0.0+incompatible
k8s.io/ingress-nginx v0.0.0-20200903213136-333288e755c1
k8s.io/kubernetes v1.19.0
sigs.k8s.io/controller-runtime v0.6.1-0.20200831170621-ab55aa710b06
)
// Pinned to kubernetes-1.16.2

View File

@ -0,0 +1,14 @@
## How to use codegen
```sh
./hack/k8s/codegen/update-generated.sh
```
It should print:
```
Generating deepcopy funcs
Generating clientset for etcd:v1beta2 at github.com/coreos/etcd-operator/pkg/generated/clientset
Generating listers for etcd:v1beta2 at github.com/coreos/etcd-operator/pkg/generated/listers
Generating informers for etcd:v1beta2 at github.com/coreos/etcd-operator/pkg/generated/informers
```

View File

@ -0,0 +1,17 @@
// RAINBOND, Application Management Platform
// Copyright (C) 2014-2020 Goodrain Co., Ltd.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. For any non-GPL usage of Rainbond,
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
// must be obtained first.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
vendor/k8s.io/code-generator/generate-groups.sh \
"all" \
"github.com/goodrain/rainbond-operator/pkg/generated" \
"github.com/goodrain/rainbond-operator/pkg/apis" \
"rainbond:v1alpha1" \
--go-header-file "./hack/k8s/codegen/boilerplate.go.txt" \
$@

View File

@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
if ! output=$(./hack/k8s/codegen/update-generated.sh --verify-only 2>&1); then
echo "FAILURE: verification of codegen failed:"
echo "${output}"
exit 1
fi
echo "Verified generated code ==="

View File

@ -0,0 +1,36 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package v1alpha1 contains API Schema definitions for the rainbond v1alpha1 API group
// +kubebuilder:object:generate=true
// +groupName=rainbond.goodrdain.io
package v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "rainbond.goodrdain.io", Version: "v1alpha1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)

View File

@ -0,0 +1,170 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// The status of helm app
// Except for `not-configured`, the other statues are the native statues of helm.
type HelmAppStatusStatus string
// The status of helm app
const (
HelmAppStatusNotConfigured HelmAppStatusStatus = "not-configured"
// HelmAppStatusunknown indicates that a release is in an uncertain state.
HelmAppStatusunknown HelmAppStatusStatus = "unknown"
// HelmAppStatusDeployed indicates that the release has been pushed to Kubernetes.
HelmAppStatusDeployed HelmAppStatusStatus = "deployed"
// HelmAppStatusUninstalled indicates that a release has been uninstalled from Kubernetes.
HelmAppStatusUninstalled HelmAppStatusStatus = "uninstalled"
// HelmAppStatusSuperseded indicates that this release object is outdated and a newer one exists.
HelmAppStatusSuperseded HelmAppStatusStatus = "superseded"
// HelmAppStatusFailed indicates that the release was not successfully deployed.
HelmAppStatusFailed HelmAppStatusStatus = "failed"
// HelmAppStatusUninstalling indicates that a uninstall operation is underway.
HelmAppStatusUninstalling HelmAppStatusStatus = "uninstalling"
// HelmAppStatusPendingInstall indicates that an install operation is underway.
HelmAppStatusPendingInstall HelmAppStatusStatus = "pending-install"
// HelmAppStatusPendingUpgrade indicates that an upgrade operation is underway.
HelmAppStatusPendingUpgrade HelmAppStatusStatus = "pending-upgrade"
// HelmAppStatusPendingRollback indicates that an rollback operation is underway.
HelmAppStatusPendingRollback HelmAppStatusStatus = "pending-rollback"
)
// RbdComponentConditionType is a valid value for RbdComponentCondition.Type
type HelmAppConditionType string
// These are valid conditions of helm app.
const (
// HelmAppDetected indicates whether the helm app has been detected.
HelmAppDetected HelmAppConditionType = "Detected"
// HelmAppDetected indicates whether the helm app has been installed.
HelmAppInstalled HelmAppConditionType = "Installed"
)
// HelmAppCondition contains details for the current condition of this helm application.
type HelmAppCondition struct {
// Type is the type of the condition.
Type HelmAppConditionType `json:"type" protobuf:"bytes,1,opt,name=type,casttype=PodConditionType"`
// Status is the status of the condition.
// Can be True, False, Unknown.
// More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions
Status corev1.ConditionStatus `json:"status" protobuf:"bytes,2,opt,name=status,casttype=ConditionStatus"`
// Last time the condition transitioned from one status to another.
// +optional
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" protobuf:"bytes,4,opt,name=lastTransitionTime"`
// Unique, one-word, CamelCase reason for the condition's last transition.
// +optional
Reason string `json:"reason,omitempty" protobuf:"bytes,5,opt,name=reason"`
// Human-readable message indicating details about last transition.
// +optional
Message string `json:"message,omitempty" protobuf:"bytes,6,opt,name=message"`
}
// HelmAppSpec defines the desired state of HelmApp
type HelmAppSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
// The prerequisite status.
// +kubebuilder:validation:Enum=NotConfigured;Configured
PreStatus string `json:"preStatus,omitempty"`
// The application name.
// TODO: validation
AppName string `json:"appName"`
// The application version.
// TODO: validation
Version string `json:"version"`
// The application revision.
Revision *int32 `json:"revision"`
// The values.yaml of the helm app, encoded by base64.
Values string `json:"values"`
}
// HelmAppStore represents a helm repo.
type HelmAppStore struct {
// The verision of the helm app store.
Version string `json:"version"`
Name string `json:"name"`
// The url of helm repo, sholud be a helm native repo url or a git url.
URL string `json:"url"`
// The branch of a git repo.
Branch string `json:"branch,omitempty"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
}
// HelmAppStatus defines the observed state of HelmApp
type HelmAppStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
// The status of helm app.
Status HelmAppStatusStatus `json:"status"`
// Current state of helm app.
Contidions []HelmAppCondition `json:"conditions,omitempty"`
CurrentValues string `json:"currentValues,omitempty"`
CurrentRevision string `json:"currentRevision,omitempty"`
}
// +kubebuilder:object:root=true
// HelmApp is the Schema for the helmapps API
type HelmApp struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec HelmAppSpec `json:"spec,omitempty"`
Status HelmAppStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// HelmAppList contains a list of HelmApp
type HelmAppList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []HelmApp `json:"items"`
}
func init() {
SchemeBuilder.Register(&HelmApp{}, &HelmAppList{})
}

View File

@ -0,0 +1,157 @@
// +build !ignore_autogenerated
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HelmApp) DeepCopyInto(out *HelmApp) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmApp.
func (in *HelmApp) DeepCopy() *HelmApp {
if in == nil {
return nil
}
out := new(HelmApp)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *HelmApp) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HelmAppCondition) DeepCopyInto(out *HelmAppCondition) {
*out = *in
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmAppCondition.
func (in *HelmAppCondition) DeepCopy() *HelmAppCondition {
if in == nil {
return nil
}
out := new(HelmAppCondition)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HelmAppList) DeepCopyInto(out *HelmAppList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]HelmApp, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmAppList.
func (in *HelmAppList) DeepCopy() *HelmAppList {
if in == nil {
return nil
}
out := new(HelmAppList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *HelmAppList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HelmAppSpec) DeepCopyInto(out *HelmAppSpec) {
*out = *in
if in.Revision != nil {
in, out := &in.Revision, &out.Revision
*out = new(int32)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmAppSpec.
func (in *HelmAppSpec) DeepCopy() *HelmAppSpec {
if in == nil {
return nil
}
out := new(HelmAppSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HelmAppStatus) DeepCopyInto(out *HelmAppStatus) {
*out = *in
if in.Contidions != nil {
in, out := &in.Contidions, &out.Contidions
*out = make([]HelmAppCondition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmAppStatus.
func (in *HelmAppStatus) DeepCopy() *HelmAppStatus {
if in == nil {
return nil
}
out := new(HelmAppStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HelmAppStore) DeepCopyInto(out *HelmAppStore) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HelmAppStore.
func (in *HelmAppStore) DeepCopy() *HelmAppStore {
if in == nil {
return nil
}
out := new(HelmAppStore)
in.DeepCopyInto(out)
return out
}