...

Source file src/github.com/chaos-mesh/chaos-mesh/pkg/dashboard/store/experiment/experiment.go

Documentation: github.com/chaos-mesh/chaos-mesh/pkg/dashboard/store/experiment

     1  // Copyright 2021 Chaos Mesh Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  
    16  package experiment
    17  
    18  import (
    19  	"context"
    20  	"time"
    21  
    22  	"github.com/jinzhu/gorm"
    23  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    24  	ctrl "sigs.k8s.io/controller-runtime"
    25  
    26  	"github.com/chaos-mesh/chaos-mesh/api/v1alpha1"
    27  	"github.com/chaos-mesh/chaos-mesh/pkg/dashboard/core"
    28  )
    29  
    30  var log = ctrl.Log.WithName("store/experiment")
    31  
    32  // NewStore returns a new ExperimentStore.
    33  func NewStore(db *gorm.DB) core.ExperimentStore {
    34  	db.AutoMigrate(&core.Experiment{})
    35  
    36  	return &experimentStore{db}
    37  }
    38  
    39  // DeleteIncompleteExperiments call core.ExperimentStore.DeleteIncompleteExperiments to deletes all incomplete experiments.
    40  func DeleteIncompleteExperiments(es core.ExperimentStore, _ core.EventStore) {
    41  	if err := es.DeleteIncompleteExperiments(context.Background()); err != nil && !gorm.IsRecordNotFoundError(err) {
    42  		log.Error(err, "failed to delete all incomplete experiments")
    43  	}
    44  }
    45  
    46  type experimentStore struct {
    47  	db *gorm.DB
    48  }
    49  
    50  // ListMeta implements the core.ExperimentStore.ListMeta method.
    51  func (e *experimentStore) ListMeta(_ context.Context, kind, namespace, name string, archived bool) ([]*core.ExperimentMeta, error) {
    52  	db := e.db.Table("experiments")
    53  	experiments := make([]*core.ExperimentMeta, 0)
    54  	query, args := constructQueryArgs(kind, namespace, name, "")
    55  
    56  	if err := db.Where(query, args).Where("archived = ?", archived).Find(&experiments).Error; err != nil && !gorm.IsRecordNotFoundError(err) {
    57  		return nil, err
    58  	}
    59  
    60  	return experiments, nil
    61  }
    62  
    63  // FindByUID implements the core.ExperimentStore.FindByUID method.
    64  func (e *experimentStore) FindByUID(_ context.Context, uid string) (*core.Experiment, error) {
    65  	experiment := new(core.Experiment)
    66  
    67  	if err := e.db.Where("uid = ?", uid).First(experiment).Error; err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	return experiment, nil
    72  }
    73  
    74  // FindMetaByUID implements the core.ExperimentStore.FindMetaByUID method.
    75  func (e *experimentStore) FindMetaByUID(_ context.Context, uid string) (*core.ExperimentMeta, error) {
    76  	db := e.db.Table("experiments")
    77  	experiment := new(core.ExperimentMeta)
    78  
    79  	if err := db.Where("uid = ?", uid).First(experiment).Error; err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	return experiment, nil
    84  }
    85  
    86  // FindManagedByNamespaceName implements the core.ExperimentStore.FindManagedByNamespaceName method.
    87  func (e *experimentStore) FindManagedByNamespaceName(_ context.Context, namespace, name string) ([]*core.Experiment, error) {
    88  	experiments := make([]*core.Experiment, 0)
    89  	if err := e.db.Model(core.Experiment{}).
    90  		Find(&experiments, "namespace = ? AND name = ? AND archived = ?", namespace, name, true).Error; err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	managedExperiments := make([]*core.Experiment, 0)
    95  	for _, expr := range experiments {
    96  		meta := &unstructured.Unstructured{}
    97  		if err := meta.UnmarshalJSON([]byte(expr.Experiment)); err != nil {
    98  			return nil, err
    99  		}
   100  
   101  		if meta.GetLabels()[v1alpha1.LabelManagedBy] != "" {
   102  			managedExperiments = append(managedExperiments, expr)
   103  		}
   104  	}
   105  
   106  	if len(managedExperiments) == 0 {
   107  		return nil, gorm.ErrRecordNotFound
   108  	}
   109  
   110  	return managedExperiments, nil
   111  }
   112  
   113  // Set implements the core.ExperimentStore.Set method.
   114  func (e *experimentStore) Set(_ context.Context, experiment *core.Experiment) error {
   115  	return e.db.Model(core.Experiment{}).Save(experiment).Error
   116  }
   117  
   118  // Archive implements the core.ExperimentStore.Archive method.
   119  func (e *experimentStore) Archive(_ context.Context, namespace, name string) error {
   120  	if err := e.db.Model(core.Experiment{}).
   121  		Where("namespace = ? AND name = ? AND archived = ?", namespace, name, false).
   122  		Updates(map[string]interface{}{"archived": true, "finish_time": time.Now()}).Error; err != nil && !gorm.IsRecordNotFoundError(err) {
   123  		return err
   124  	}
   125  
   126  	return nil
   127  }
   128  
   129  // Delete deletes the experiment from the datastore.
   130  func (e *experimentStore) Delete(_ context.Context, exp *core.Experiment) error {
   131  	err := e.db.Table("experiments").Unscoped().Delete(*exp).Error
   132  	return err
   133  }
   134  
   135  // DeleteByFinishTime deletes experiments whose time difference is greater than the given time from FinishTime.
   136  func (e *experimentStore) DeleteByFinishTime(_ context.Context, ttl time.Duration) error {
   137  	experiments, err := e.ListMeta(context.Background(), "", "", "", true)
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	nowTime := time.Now()
   143  	for _, exp := range experiments {
   144  		if exp.FinishTime == nil {
   145  			log.Error(nil, "finish time is nil when deleting archived experiment records, skip it", "experiment", exp)
   146  			continue
   147  		}
   148  		if exp.FinishTime.Add(ttl).Before(nowTime) {
   149  			if err := e.db.Table("experiments").Unscoped().Delete(*exp).Error; err != nil {
   150  				return err
   151  			}
   152  		}
   153  	}
   154  
   155  	return nil
   156  }
   157  
   158  // DeleteByUIDs deletes archives by the uid list.
   159  func (e *experimentStore) DeleteByUIDs(_ context.Context, uids []string) error {
   160  	return e.db.Table("experiments").Where("uid IN (?)", uids).Unscoped().Delete(core.Experiment{}).Error
   161  }
   162  
   163  // DeleteIncompleteExperiments implements the core.ExperimentStore.DeleteIncompleteExperiments method.
   164  func (e *experimentStore) DeleteIncompleteExperiments(_ context.Context) error {
   165  	return e.db.Where("finish_time IS NULL").Unscoped().Delete(core.Experiment{}).Error
   166  }
   167  
   168  func constructQueryArgs(kind, ns, name, uid string) (string, []string) {
   169  	query := ""
   170  	args := make([]string, 0)
   171  
   172  	if kind != "" {
   173  		query += "kind = ?"
   174  		args = append(args, kind)
   175  	}
   176  
   177  	if ns != "" {
   178  		if len(args) > 0 {
   179  			query += " AND namespace = ?"
   180  		} else {
   181  			query += "namespace = ?"
   182  		}
   183  		args = append(args, ns)
   184  	}
   185  
   186  	if name != "" {
   187  		if len(args) > 0 {
   188  			query += " AND name = ?"
   189  		} else {
   190  			query += "name = ?"
   191  		}
   192  		args = append(args, name)
   193  	}
   194  
   195  	if uid != "" {
   196  		if len(args) > 0 {
   197  			query += " AND uid = ?"
   198  		} else {
   199  			query += "uid = ?"
   200  		}
   201  		args = append(args, uid)
   202  	}
   203  
   204  	return query, args
   205  }
   206