...

Source file src/github.com/chaos-mesh/chaos-mesh/pkg/time/time_skew_linux.go

Documentation: github.com/chaos-mesh/chaos-mesh/pkg/time

     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 time
    17  
    18  import (
    19  	"fmt"
    20  	"sync"
    21  
    22  	"github.com/go-logr/logr"
    23  	"github.com/pkg/errors"
    24  
    25  	"github.com/chaos-mesh/chaos-mesh/pkg/cerr"
    26  	"github.com/chaos-mesh/chaos-mesh/pkg/chaosdaemon/tasks"
    27  )
    28  
    29  // clockGettimeSkewFakeImage is the filename of fake image after compiling
    30  const clockGettimeSkewFakeImage = "fake_clock_gettime.o"
    31  
    32  // clockGettime is the target function would be replaced
    33  const clockGettime = "clock_gettime"
    34  
    35  // These three consts corresponding to the three extern variables in the fake_clock_gettime.c
    36  const (
    37  	externVarClockIdsMask = "CLOCK_IDS_MASK"
    38  	externVarTvSecDelta   = "TV_SEC_DELTA"
    39  	externVarTvNsecDelta  = "TV_NSEC_DELTA"
    40  )
    41  
    42  // timeofdaySkewFakeImage is the filename of fake image after compiling
    43  const timeOfDaySkewFakeImage = "fake_gettimeofday.o"
    44  
    45  // getTimeOfDay is the target function would be replaced
    46  const getTimeOfDay = "gettimeofday"
    47  
    48  // Config is the summary config of get_time_of_day and clock_get_time.
    49  // Config here is only for injector of k8s pod.
    50  // We divide group injector on linux process , pod injector for k8s and
    51  // the base injector , so we can simply create another config struct just
    52  // for linux process for chaos-mesh/chaosd or watchmaker.
    53  type Config struct {
    54  	deltaSeconds     int64
    55  	deltaNanoSeconds int64
    56  	clockIDsMask     uint64
    57  }
    58  
    59  func NewConfig(deltaSeconds int64, deltaNanoSeconds int64, clockIDsMask uint64) Config {
    60  	return Config{
    61  		deltaSeconds:     deltaSeconds,
    62  		deltaNanoSeconds: deltaNanoSeconds,
    63  		clockIDsMask:     clockIDsMask,
    64  	}
    65  }
    66  
    67  func (c *Config) DeepCopy() tasks.Object {
    68  	return &Config{
    69  		c.deltaSeconds,
    70  		c.deltaNanoSeconds,
    71  		c.clockIDsMask,
    72  	}
    73  }
    74  
    75  // Merge implement how to merge time skew tasks.
    76  func (c *Config) Merge(a tasks.Mergeable) error {
    77  	A, OK := a.(*Config)
    78  	if OK {
    79  		// TODO: Add more reasonable merge method
    80  		c.deltaSeconds += A.deltaSeconds
    81  		c.deltaNanoSeconds += A.deltaNanoSeconds
    82  		c.clockIDsMask |= A.clockIDsMask
    83  		return nil
    84  	}
    85  	return cerr.NotType[*Config]().WrapInput(a).Err()
    86  }
    87  
    88  type ConfigCreatorParas struct {
    89  	Logger        logr.Logger
    90  	Config        Config
    91  	PodProcessMap *tasks.PodContainerNameProcessMap
    92  }
    93  
    94  // New assumes we get ConfigCreatorParas from values.
    95  // New will init a struct just like PodHandler(ProcessGroupHandler(Skew))
    96  func (c *Config) New(values interface{}) (tasks.Injectable, error) {
    97  	paras, ok := values.(ConfigCreatorParas)
    98  	if !ok {
    99  		return nil, errors.New("not ConfigCreatorParas")
   100  	}
   101  
   102  	skew, err := GetSkew(paras.Logger, paras.Config)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	newGroupProcessHandler :=
   108  		tasks.NewProcessGroupHandler(paras.Logger, &skew)
   109  	newPodHandler := tasks.NewPodHandler(paras.PodProcessMap,
   110  		&newGroupProcessHandler, paras.Logger)
   111  	return &newPodHandler, nil
   112  }
   113  
   114  // Assign assumes the input injectable is *tasks.PodHandler.
   115  // We also assume the SubProcess of podHandler is *tasks.ProcessGroupHandler
   116  // and the LeaderProcess of ProcessGroupHandler is *Skew.
   117  func (c *Config) Assign(injectable tasks.Injectable) error {
   118  	podHandler, ok := injectable.(*tasks.PodHandler)
   119  	if !ok {
   120  		return errors.New(fmt.Sprintf("type %T is not *tasks.PodHandler", injectable))
   121  	}
   122  	groupProcessHandler, ok := podHandler.SubProcess.(*tasks.ProcessGroupHandler)
   123  	if !ok {
   124  		return errors.New(fmt.Sprintf("type %T is not *tasks.ProcessGroupHandler", podHandler.SubProcess))
   125  	}
   126  	I, ok := groupProcessHandler.LeaderProcess.(*Skew)
   127  	if !ok {
   128  		return errors.New(fmt.Sprintf("type %T is not *Skew", groupProcessHandler.LeaderProcess))
   129  	}
   130  
   131  	I.SkewConfig = *c
   132  	return nil
   133  }
   134  
   135  // Skew implements ChaosOnProcessGroup.
   136  // We locked Skew injecting and recovering to avoid conflict.
   137  type Skew struct {
   138  	SkewConfig   Config
   139  	clockGetTime *FakeImage
   140  	getTimeOfDay *FakeImage
   141  
   142  	locker sync.Mutex
   143  	logger logr.Logger
   144  }
   145  
   146  func GetSkew(logger logr.Logger, c Config) (Skew, error) {
   147  	clockGetTimeImage, err := LoadFakeImageFromEmbedFs(clockGettimeSkewFakeImage, clockGettime, logger)
   148  	if err != nil {
   149  		return Skew{}, errors.Wrap(err, "load fake image")
   150  	}
   151  
   152  	getTimeOfDayimage, err := LoadFakeImageFromEmbedFs(timeOfDaySkewFakeImage, getTimeOfDay, logger)
   153  	if err != nil {
   154  		return Skew{}, errors.Wrap(err, "load fake image")
   155  	}
   156  
   157  	return Skew{
   158  		SkewConfig:   c,
   159  		clockGetTime: clockGetTimeImage,
   160  		getTimeOfDay: getTimeOfDayimage,
   161  		locker:       sync.Mutex{},
   162  		logger:       logger,
   163  	}, nil
   164  }
   165  
   166  func (s *Skew) Fork() (tasks.ChaosOnProcessGroup, error) {
   167  	// TODO : to KEAO can I share FakeImage between threads?
   168  	skew, err := GetSkew(s.logger, s.SkewConfig)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	return &skew, nil
   174  }
   175  
   176  func (s *Skew) Assign(injectable tasks.Injectable) error {
   177  	I, OK := injectable.(*Skew)
   178  	if OK {
   179  		I.SkewConfig = *s.SkewConfig.DeepCopy().(*Config)
   180  		return nil
   181  	}
   182  	return cerr.NotType[*Skew]().WrapInput(injectable).Err()
   183  }
   184  
   185  func (s *Skew) Inject(pid tasks.IsID) error {
   186  	s.locker.Lock()
   187  	defer s.locker.Unlock()
   188  	sysPID, ok := pid.(tasks.SysPID)
   189  	if !ok {
   190  		return tasks.ErrNotTypeSysID.WrapInput(pid).Err()
   191  	}
   192  
   193  	s.logger.Info("injecting time skew", "pid", pid)
   194  
   195  	err := s.clockGetTime.AttachToProcess(int(sysPID), map[string]uint64{
   196  		externVarClockIdsMask: s.SkewConfig.clockIDsMask,
   197  		externVarTvSecDelta:   uint64(s.SkewConfig.deltaSeconds),
   198  		externVarTvNsecDelta:  uint64(s.SkewConfig.deltaNanoSeconds),
   199  	})
   200  	if err != nil {
   201  		return err
   202  	}
   203  
   204  	err = s.getTimeOfDay.AttachToProcess(int(sysPID), map[string]uint64{
   205  		externVarTvSecDelta:  uint64(s.SkewConfig.deltaSeconds),
   206  		externVarTvNsecDelta: uint64(s.SkewConfig.deltaNanoSeconds),
   207  	})
   208  	if err != nil {
   209  		return err
   210  	}
   211  	return nil
   212  }
   213  
   214  // Recover clock_get_time & get_time_of_day one by one ,
   215  // if error comes from clock_get_time.Recover we will continue recover another fake image
   216  // and merge errors.
   217  func (s *Skew) Recover(pid tasks.IsID) error {
   218  	s.locker.Lock()
   219  	defer s.locker.Unlock()
   220  	sysPID, ok := pid.(tasks.SysPID)
   221  	if !ok {
   222  		return tasks.ErrNotTypeSysID.WrapInput(pid).Err()
   223  	}
   224  
   225  	s.logger.Info("recovering time skew", "pid", pid)
   226  
   227  	err1 := s.clockGetTime.Recover(int(sysPID), map[string]uint64{
   228  		externVarClockIdsMask: s.SkewConfig.clockIDsMask,
   229  		externVarTvSecDelta:   uint64(s.SkewConfig.deltaSeconds),
   230  		externVarTvNsecDelta:  uint64(s.SkewConfig.deltaNanoSeconds),
   231  	})
   232  	if err1 != nil {
   233  		err2 := s.getTimeOfDay.Recover(int(sysPID), map[string]uint64{
   234  			externVarTvSecDelta:  uint64(s.SkewConfig.deltaSeconds),
   235  			externVarTvNsecDelta: uint64(s.SkewConfig.deltaNanoSeconds),
   236  		})
   237  		if err2 != nil {
   238  			return errors.Wrapf(err1, "time skew all failed %v", err2)
   239  		}
   240  		return err1
   241  	}
   242  
   243  	err2 := s.getTimeOfDay.Recover(int(sysPID), map[string]uint64{
   244  		externVarTvSecDelta:  uint64(s.SkewConfig.deltaSeconds),
   245  		externVarTvNsecDelta: uint64(s.SkewConfig.deltaNanoSeconds),
   246  	})
   247  	if err2 != nil {
   248  		return err2
   249  	}
   250  
   251  	return nil
   252  }
   253