github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/states/statefile/version4.go (about)

     1  package statefile
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"sort"
     8  
     9  	version "github.com/hashicorp/go-version"
    10  	ctyjson "github.com/zclconf/go-cty/cty/json"
    11  
    12  	"github.com/hashicorp/terraform/addrs"
    13  	"github.com/hashicorp/terraform/states"
    14  	"github.com/hashicorp/terraform/tfdiags"
    15  )
    16  
    17  func readStateV4(src []byte) (*File, tfdiags.Diagnostics) {
    18  	var diags tfdiags.Diagnostics
    19  	sV4 := &stateV4{}
    20  	err := json.Unmarshal(src, sV4)
    21  	if err != nil {
    22  		diags = diags.Append(jsonUnmarshalDiags(err))
    23  		return nil, diags
    24  	}
    25  
    26  	file, prepDiags := prepareStateV4(sV4)
    27  	diags = diags.Append(prepDiags)
    28  	return file, diags
    29  }
    30  
    31  func prepareStateV4(sV4 *stateV4) (*File, tfdiags.Diagnostics) {
    32  	var diags tfdiags.Diagnostics
    33  
    34  	var tfVersion *version.Version
    35  	if sV4.TerraformVersion != "" {
    36  		var err error
    37  		tfVersion, err = version.NewVersion(sV4.TerraformVersion)
    38  		if err != nil {
    39  			diags = diags.Append(tfdiags.Sourceless(
    40  				tfdiags.Error,
    41  				"Invalid Terraform version string",
    42  				fmt.Sprintf("State file claims to have been written by Terraform version %q, which is not a valid version string.", sV4.TerraformVersion),
    43  			))
    44  		}
    45  	}
    46  
    47  	file := &File{
    48  		TerraformVersion: tfVersion,
    49  		Serial:           sV4.Serial,
    50  		Lineage:          sV4.Lineage,
    51  	}
    52  
    53  	state := states.NewState()
    54  
    55  	for _, rsV4 := range sV4.Resources {
    56  		rAddr := addrs.Resource{
    57  			Type: rsV4.Type,
    58  			Name: rsV4.Name,
    59  		}
    60  		switch rsV4.Mode {
    61  		case "managed":
    62  			rAddr.Mode = addrs.ManagedResourceMode
    63  		case "data":
    64  			rAddr.Mode = addrs.DataResourceMode
    65  		default:
    66  			diags = diags.Append(tfdiags.Sourceless(
    67  				tfdiags.Error,
    68  				"Invalid resource mode in state",
    69  				fmt.Sprintf("State contains a resource with mode %q (%q %q) which is not supported.", rsV4.Mode, rAddr.Type, rAddr.Name),
    70  			))
    71  			continue
    72  		}
    73  
    74  		moduleAddr := addrs.RootModuleInstance
    75  		if rsV4.Module != "" {
    76  			var addrDiags tfdiags.Diagnostics
    77  			moduleAddr, addrDiags = addrs.ParseModuleInstanceStr(rsV4.Module)
    78  			diags = diags.Append(addrDiags)
    79  			if addrDiags.HasErrors() {
    80  				continue
    81  			}
    82  		}
    83  
    84  		providerAddr, addrDiags := addrs.ParseAbsProviderConfigStr(rsV4.ProviderConfig)
    85  		diags.Append(addrDiags)
    86  		if addrDiags.HasErrors() {
    87  			continue
    88  		}
    89  
    90  		var eachMode states.EachMode
    91  		switch rsV4.EachMode {
    92  		case "":
    93  			eachMode = states.NoEach
    94  		case "list":
    95  			eachMode = states.EachList
    96  		case "map":
    97  			eachMode = states.EachMap
    98  		default:
    99  			diags = diags.Append(tfdiags.Sourceless(
   100  				tfdiags.Error,
   101  				"Invalid resource metadata in state",
   102  				fmt.Sprintf("Resource %s has invalid \"each\" value %q in state.", rAddr.Absolute(moduleAddr), eachMode),
   103  			))
   104  			continue
   105  		}
   106  
   107  		ms := state.EnsureModule(moduleAddr)
   108  
   109  		// Ensure the resource container object is present in the state.
   110  		ms.SetResourceMeta(rAddr, eachMode, providerAddr)
   111  
   112  		for _, isV4 := range rsV4.Instances {
   113  			keyRaw := isV4.IndexKey
   114  			var key addrs.InstanceKey
   115  			switch tk := keyRaw.(type) {
   116  			case int:
   117  				key = addrs.IntKey(tk)
   118  			case float64:
   119  				// Since JSON only has one number type, reading from encoding/json
   120  				// gives us a float64 here even if the number is whole.
   121  				// float64 has a smaller integer range than int, but in practice
   122  				// we rarely have more than a few tens of instances and so
   123  				// it's unlikely that we'll exhaust the 52 bits in a float64.
   124  				key = addrs.IntKey(int(tk))
   125  			case string:
   126  				key = addrs.StringKey(tk)
   127  			default:
   128  				if keyRaw != nil {
   129  					diags = diags.Append(tfdiags.Sourceless(
   130  						tfdiags.Error,
   131  						"Invalid resource instance metadata in state",
   132  						fmt.Sprintf("Resource %s has an instance with the invalid instance key %#v.", rAddr.Absolute(moduleAddr), keyRaw),
   133  					))
   134  					continue
   135  				}
   136  				key = addrs.NoKey
   137  			}
   138  
   139  			instAddr := rAddr.Instance(key)
   140  
   141  			obj := &states.ResourceInstanceObjectSrc{
   142  				SchemaVersion: isV4.SchemaVersion,
   143  			}
   144  
   145  			{
   146  				// Instance attributes
   147  				switch {
   148  				case isV4.AttributesRaw != nil:
   149  					obj.AttrsJSON = isV4.AttributesRaw
   150  				case isV4.AttributesFlat != nil:
   151  					obj.AttrsFlat = isV4.AttributesFlat
   152  				default:
   153  					// This is odd, but we'll accept it and just treat the
   154  					// object has being empty. In practice this should arise
   155  					// only from the contrived sort of state objects we tend
   156  					// to hand-write inline in tests.
   157  					obj.AttrsJSON = []byte{'{', '}'}
   158  				}
   159  			}
   160  
   161  			{
   162  				// Status
   163  				raw := isV4.Status
   164  				switch raw {
   165  				case "":
   166  					obj.Status = states.ObjectReady
   167  				case "tainted":
   168  					obj.Status = states.ObjectTainted
   169  				default:
   170  					diags = diags.Append(tfdiags.Sourceless(
   171  						tfdiags.Error,
   172  						"Invalid resource instance metadata in state",
   173  						fmt.Sprintf("Instance %s has invalid status %q.", instAddr.Absolute(moduleAddr), raw),
   174  					))
   175  					continue
   176  				}
   177  			}
   178  
   179  			if raw := isV4.PrivateRaw; len(raw) > 0 {
   180  				obj.Private = raw
   181  			}
   182  
   183  			{
   184  				// Allow both the deprecated `depends_on` and new
   185  				// `dependencies` to coexist for now so resources can be
   186  				// upgraded as they are refreshed.
   187  				depsRaw := isV4.DependsOn
   188  				deps := make([]addrs.Referenceable, 0, len(depsRaw))
   189  				for _, depRaw := range depsRaw {
   190  					ref, refDiags := addrs.ParseRefStr(depRaw)
   191  					diags = diags.Append(refDiags)
   192  					if refDiags.HasErrors() {
   193  						continue
   194  					}
   195  					if len(ref.Remaining) != 0 {
   196  						diags = diags.Append(tfdiags.Sourceless(
   197  							tfdiags.Error,
   198  							"Invalid resource instance metadata in state",
   199  							fmt.Sprintf("Instance %s declares dependency on %q, which is not a reference to a dependable object.", instAddr.Absolute(moduleAddr), depRaw),
   200  						))
   201  					}
   202  					if ref.Subject == nil {
   203  						// Should never happen
   204  						panic(fmt.Sprintf("parsing dependency %q for instance %s returned a nil address", depRaw, instAddr.Absolute(moduleAddr)))
   205  					}
   206  					deps = append(deps, ref.Subject)
   207  				}
   208  				obj.DependsOn = deps
   209  			}
   210  
   211  			{
   212  				depsRaw := isV4.Dependencies
   213  				deps := make([]addrs.AbsResource, 0, len(depsRaw))
   214  				for _, depRaw := range depsRaw {
   215  					addr, addrDiags := addrs.ParseAbsResourceStr(depRaw)
   216  					diags = diags.Append(addrDiags)
   217  					if addrDiags.HasErrors() {
   218  						continue
   219  					}
   220  					deps = append(deps, addr)
   221  				}
   222  				obj.Dependencies = deps
   223  			}
   224  
   225  			switch {
   226  			case isV4.Deposed != "":
   227  				dk := states.DeposedKey(isV4.Deposed)
   228  				if len(dk) != 8 {
   229  					diags = diags.Append(tfdiags.Sourceless(
   230  						tfdiags.Error,
   231  						"Invalid resource instance metadata in state",
   232  						fmt.Sprintf("Instance %s has an object with deposed key %q, which is not correctly formatted.", instAddr.Absolute(moduleAddr), isV4.Deposed),
   233  					))
   234  					continue
   235  				}
   236  				is := ms.ResourceInstance(instAddr)
   237  				if is.HasDeposed(dk) {
   238  					diags = diags.Append(tfdiags.Sourceless(
   239  						tfdiags.Error,
   240  						"Duplicate resource instance in state",
   241  						fmt.Sprintf("Instance %s deposed object %q appears multiple times in the state file.", instAddr.Absolute(moduleAddr), dk),
   242  					))
   243  					continue
   244  				}
   245  
   246  				ms.SetResourceInstanceDeposed(instAddr, dk, obj, providerAddr)
   247  			default:
   248  				is := ms.ResourceInstance(instAddr)
   249  				if is.HasCurrent() {
   250  					diags = diags.Append(tfdiags.Sourceless(
   251  						tfdiags.Error,
   252  						"Duplicate resource instance in state",
   253  						fmt.Sprintf("Instance %s appears multiple times in the state file.", instAddr.Absolute(moduleAddr)),
   254  					))
   255  					continue
   256  				}
   257  
   258  				ms.SetResourceInstanceCurrent(instAddr, obj, providerAddr)
   259  			}
   260  		}
   261  
   262  		// We repeat this after creating the instances because
   263  		// SetResourceInstanceCurrent automatically resets this metadata based
   264  		// on the incoming objects. That behavior is useful when we're making
   265  		// piecemeal updates to the state during an apply, but when we're
   266  		// reading the state file we want to reflect its contents exactly.
   267  		ms.SetResourceMeta(rAddr, eachMode, providerAddr)
   268  	}
   269  
   270  	// The root module is special in that we persist its attributes and thus
   271  	// need to reload them now. (For descendent modules we just re-calculate
   272  	// them based on the latest configuration on each run.)
   273  	{
   274  		rootModule := state.RootModule()
   275  		for name, fos := range sV4.RootOutputs {
   276  			os := &states.OutputValue{}
   277  			os.Sensitive = fos.Sensitive
   278  
   279  			ty, err := ctyjson.UnmarshalType([]byte(fos.ValueTypeRaw))
   280  			if err != nil {
   281  				diags = diags.Append(tfdiags.Sourceless(
   282  					tfdiags.Error,
   283  					"Invalid output value type in state",
   284  					fmt.Sprintf("The state file has an invalid type specification for output %q: %s.", name, err),
   285  				))
   286  				continue
   287  			}
   288  
   289  			val, err := ctyjson.Unmarshal([]byte(fos.ValueRaw), ty)
   290  			if err != nil {
   291  				diags = diags.Append(tfdiags.Sourceless(
   292  					tfdiags.Error,
   293  					"Invalid output value saved in state",
   294  					fmt.Sprintf("The state file has an invalid value for output %q: %s.", name, err),
   295  				))
   296  				continue
   297  			}
   298  
   299  			os.Value = val
   300  			rootModule.OutputValues[name] = os
   301  		}
   302  	}
   303  
   304  	file.State = state
   305  	return file, diags
   306  }
   307  
   308  func writeStateV4(file *File, w io.Writer) tfdiags.Diagnostics {
   309  	// Here we'll convert back from the "File" representation to our
   310  	// stateV4 struct representation and write that.
   311  	//
   312  	// While we support legacy state formats for reading, we only support the
   313  	// latest for writing and so if a V5 is added in future then this function
   314  	// should be deleted and replaced with a writeStateV5, even though the
   315  	// read/prepare V4 functions above would stick around.
   316  
   317  	var diags tfdiags.Diagnostics
   318  	if file == nil || file.State == nil {
   319  		panic("attempt to write nil state to file")
   320  	}
   321  
   322  	var terraformVersion string
   323  	if file.TerraformVersion != nil {
   324  		terraformVersion = file.TerraformVersion.String()
   325  	}
   326  
   327  	sV4 := &stateV4{
   328  		TerraformVersion: terraformVersion,
   329  		Serial:           file.Serial,
   330  		Lineage:          file.Lineage,
   331  		RootOutputs:      map[string]outputStateV4{},
   332  		Resources:        []resourceStateV4{},
   333  	}
   334  
   335  	for name, os := range file.State.RootModule().OutputValues {
   336  		src, err := ctyjson.Marshal(os.Value, os.Value.Type())
   337  		if err != nil {
   338  			diags = diags.Append(tfdiags.Sourceless(
   339  				tfdiags.Error,
   340  				"Failed to serialize output value in state",
   341  				fmt.Sprintf("An error occured while serializing output value %q: %s.", name, err),
   342  			))
   343  			continue
   344  		}
   345  
   346  		typeSrc, err := ctyjson.MarshalType(os.Value.Type())
   347  		if err != nil {
   348  			diags = diags.Append(tfdiags.Sourceless(
   349  				tfdiags.Error,
   350  				"Failed to serialize output value in state",
   351  				fmt.Sprintf("An error occured while serializing the type of output value %q: %s.", name, err),
   352  			))
   353  			continue
   354  		}
   355  
   356  		sV4.RootOutputs[name] = outputStateV4{
   357  			Sensitive:    os.Sensitive,
   358  			ValueRaw:     json.RawMessage(src),
   359  			ValueTypeRaw: json.RawMessage(typeSrc),
   360  		}
   361  	}
   362  
   363  	for _, ms := range file.State.Modules {
   364  		moduleAddr := ms.Addr
   365  		for _, rs := range ms.Resources {
   366  			resourceAddr := rs.Addr
   367  
   368  			var mode string
   369  			switch resourceAddr.Mode {
   370  			case addrs.ManagedResourceMode:
   371  				mode = "managed"
   372  			case addrs.DataResourceMode:
   373  				mode = "data"
   374  			default:
   375  				diags = diags.Append(tfdiags.Sourceless(
   376  					tfdiags.Error,
   377  					"Failed to serialize resource in state",
   378  					fmt.Sprintf("Resource %s has mode %s, which cannot be serialized in state", resourceAddr.Absolute(moduleAddr), resourceAddr.Mode),
   379  				))
   380  				continue
   381  			}
   382  
   383  			var eachMode string
   384  			switch rs.EachMode {
   385  			case states.NoEach:
   386  				eachMode = ""
   387  			case states.EachList:
   388  				eachMode = "list"
   389  			case states.EachMap:
   390  				eachMode = "map"
   391  			default:
   392  				diags = diags.Append(tfdiags.Sourceless(
   393  					tfdiags.Error,
   394  					"Failed to serialize resource in state",
   395  					fmt.Sprintf("Resource %s has \"each\" mode %s, which cannot be serialized in state", resourceAddr.Absolute(moduleAddr), rs.EachMode),
   396  				))
   397  				continue
   398  			}
   399  
   400  			sV4.Resources = append(sV4.Resources, resourceStateV4{
   401  				Module:         moduleAddr.String(),
   402  				Mode:           mode,
   403  				Type:           resourceAddr.Type,
   404  				Name:           resourceAddr.Name,
   405  				EachMode:       eachMode,
   406  				ProviderConfig: rs.ProviderConfig.String(),
   407  				Instances:      []instanceObjectStateV4{},
   408  			})
   409  			rsV4 := &(sV4.Resources[len(sV4.Resources)-1])
   410  
   411  			for key, is := range rs.Instances {
   412  				if is.HasCurrent() {
   413  					var objDiags tfdiags.Diagnostics
   414  					rsV4.Instances, objDiags = appendInstanceObjectStateV4(
   415  						rs, is, key, is.Current, states.NotDeposed,
   416  						rsV4.Instances,
   417  					)
   418  					diags = diags.Append(objDiags)
   419  				}
   420  				for dk, obj := range is.Deposed {
   421  					var objDiags tfdiags.Diagnostics
   422  					rsV4.Instances, objDiags = appendInstanceObjectStateV4(
   423  						rs, is, key, obj, dk,
   424  						rsV4.Instances,
   425  					)
   426  					diags = diags.Append(objDiags)
   427  				}
   428  			}
   429  		}
   430  	}
   431  
   432  	sV4.normalize()
   433  
   434  	src, err := json.MarshalIndent(sV4, "", "  ")
   435  	if err != nil {
   436  		// Shouldn't happen if we do our conversion to *stateV4 correctly above.
   437  		diags = diags.Append(tfdiags.Sourceless(
   438  			tfdiags.Error,
   439  			"Failed to serialize state",
   440  			fmt.Sprintf("An error occured while serializing the state to save it. This is a bug in Terraform and should be reported: %s.", err),
   441  		))
   442  		return diags
   443  	}
   444  	src = append(src, '\n')
   445  
   446  	_, err = w.Write(src)
   447  	if err != nil {
   448  		diags = diags.Append(tfdiags.Sourceless(
   449  			tfdiags.Error,
   450  			"Failed to write state",
   451  			fmt.Sprintf("An error occured while writing the serialized state: %s.", err),
   452  		))
   453  		return diags
   454  	}
   455  
   456  	return diags
   457  }
   458  
   459  func appendInstanceObjectStateV4(rs *states.Resource, is *states.ResourceInstance, key addrs.InstanceKey, obj *states.ResourceInstanceObjectSrc, deposed states.DeposedKey, isV4s []instanceObjectStateV4) ([]instanceObjectStateV4, tfdiags.Diagnostics) {
   460  	var diags tfdiags.Diagnostics
   461  
   462  	var status string
   463  	switch obj.Status {
   464  	case states.ObjectReady:
   465  		status = ""
   466  	case states.ObjectTainted:
   467  		status = "tainted"
   468  	default:
   469  		diags = diags.Append(tfdiags.Sourceless(
   470  			tfdiags.Error,
   471  			"Failed to serialize resource instance in state",
   472  			fmt.Sprintf("Instance %s has status %s, which cannot be saved in state.", rs.Addr.Instance(key), obj.Status),
   473  		))
   474  	}
   475  
   476  	var privateRaw []byte
   477  	if len(obj.Private) > 0 {
   478  		privateRaw = obj.Private
   479  	}
   480  
   481  	deps := make([]string, len(obj.Dependencies))
   482  	for i, depAddr := range obj.Dependencies {
   483  		deps[i] = depAddr.String()
   484  	}
   485  
   486  	depOn := make([]string, len(obj.DependsOn))
   487  	for i, depAddr := range obj.DependsOn {
   488  		depOn[i] = depAddr.String()
   489  	}
   490  
   491  	var rawKey interface{}
   492  	switch tk := key.(type) {
   493  	case addrs.IntKey:
   494  		rawKey = int(tk)
   495  	case addrs.StringKey:
   496  		rawKey = string(tk)
   497  	default:
   498  		if key != addrs.NoKey {
   499  			diags = diags.Append(tfdiags.Sourceless(
   500  				tfdiags.Error,
   501  				"Failed to serialize resource instance in state",
   502  				fmt.Sprintf("Instance %s has an unsupported instance key: %#v.", rs.Addr.Instance(key), key),
   503  			))
   504  		}
   505  	}
   506  
   507  	return append(isV4s, instanceObjectStateV4{
   508  		IndexKey:       rawKey,
   509  		Deposed:        string(deposed),
   510  		Status:         status,
   511  		SchemaVersion:  obj.SchemaVersion,
   512  		AttributesFlat: obj.AttrsFlat,
   513  		AttributesRaw:  obj.AttrsJSON,
   514  		PrivateRaw:     privateRaw,
   515  		Dependencies:   deps,
   516  		DependsOn:      depOn,
   517  	}), diags
   518  }
   519  
   520  type stateV4 struct {
   521  	Version          stateVersionV4           `json:"version"`
   522  	TerraformVersion string                   `json:"terraform_version"`
   523  	Serial           uint64                   `json:"serial"`
   524  	Lineage          string                   `json:"lineage"`
   525  	RootOutputs      map[string]outputStateV4 `json:"outputs"`
   526  	Resources        []resourceStateV4        `json:"resources"`
   527  }
   528  
   529  // normalize makes some in-place changes to normalize the way items are
   530  // stored to ensure that two functionally-equivalent states will be stored
   531  // identically.
   532  func (s *stateV4) normalize() {
   533  	sort.Stable(sortResourcesV4(s.Resources))
   534  	for _, rs := range s.Resources {
   535  		sort.Stable(sortInstancesV4(rs.Instances))
   536  	}
   537  }
   538  
   539  type outputStateV4 struct {
   540  	ValueRaw     json.RawMessage `json:"value"`
   541  	ValueTypeRaw json.RawMessage `json:"type"`
   542  	Sensitive    bool            `json:"sensitive,omitempty"`
   543  }
   544  
   545  type resourceStateV4 struct {
   546  	Module         string                  `json:"module,omitempty"`
   547  	Mode           string                  `json:"mode"`
   548  	Type           string                  `json:"type"`
   549  	Name           string                  `json:"name"`
   550  	EachMode       string                  `json:"each,omitempty"`
   551  	ProviderConfig string                  `json:"provider"`
   552  	Instances      []instanceObjectStateV4 `json:"instances"`
   553  }
   554  
   555  type instanceObjectStateV4 struct {
   556  	IndexKey interface{} `json:"index_key,omitempty"`
   557  	Status   string      `json:"status,omitempty"`
   558  	Deposed  string      `json:"deposed,omitempty"`
   559  
   560  	SchemaVersion  uint64            `json:"schema_version"`
   561  	AttributesRaw  json.RawMessage   `json:"attributes,omitempty"`
   562  	AttributesFlat map[string]string `json:"attributes_flat,omitempty"`
   563  
   564  	PrivateRaw []byte `json:"private,omitempty"`
   565  
   566  	Dependencies []string `json:"dependencies,omitempty"`
   567  	DependsOn    []string `json:"depends_on,omitempty"`
   568  }
   569  
   570  // stateVersionV4 is a weird special type we use to produce our hard-coded
   571  // "version": 4 in the JSON serialization.
   572  type stateVersionV4 struct{}
   573  
   574  func (sv stateVersionV4) MarshalJSON() ([]byte, error) {
   575  	return []byte{'4'}, nil
   576  }
   577  
   578  func (sv stateVersionV4) UnmarshalJSON([]byte) error {
   579  	// Nothing to do: we already know we're version 4
   580  	return nil
   581  }
   582  
   583  type sortResourcesV4 []resourceStateV4
   584  
   585  func (sr sortResourcesV4) Len() int      { return len(sr) }
   586  func (sr sortResourcesV4) Swap(i, j int) { sr[i], sr[j] = sr[j], sr[i] }
   587  func (sr sortResourcesV4) Less(i, j int) bool {
   588  	switch {
   589  	case sr[i].Mode != sr[j].Mode:
   590  		return sr[i].Mode < sr[j].Mode
   591  	case sr[i].Type != sr[j].Type:
   592  		return sr[i].Type < sr[j].Type
   593  	case sr[i].Name != sr[j].Name:
   594  		return sr[i].Name < sr[j].Name
   595  	default:
   596  		return false
   597  	}
   598  }
   599  
   600  type sortInstancesV4 []instanceObjectStateV4
   601  
   602  func (si sortInstancesV4) Len() int      { return len(si) }
   603  func (si sortInstancesV4) Swap(i, j int) { si[i], si[j] = si[j], si[i] }
   604  func (si sortInstancesV4) Less(i, j int) bool {
   605  	ki := si[i].IndexKey
   606  	kj := si[j].IndexKey
   607  	if ki != kj {
   608  		if (ki == nil) != (kj == nil) {
   609  			return ki == nil
   610  		}
   611  		if kii, isInt := ki.(int); isInt {
   612  			if kji, isInt := kj.(int); isInt {
   613  				return kii < kji
   614  			}
   615  			return true
   616  		}
   617  		if kis, isStr := ki.(string); isStr {
   618  			if kjs, isStr := kj.(string); isStr {
   619  				return kis < kjs
   620  			}
   621  			return true
   622  		}
   623  	}
   624  	if si[i].Deposed != si[j].Deposed {
   625  		return si[i].Deposed < si[j].Deposed
   626  	}
   627  	return false
   628  }